using System.Text; namespace CareFix.Api.Infrastructure; /// Small RFC 4180 CSV reader (quoted fields, embedded commas, quotes and new lines). public static class Csv { public static List> Read(string text) { var rows = ParseRows(text); if (rows.Count == 0) return []; var header = rows[0].Select(h => h.Trim().TrimStart('\uFEFF')).ToList(); var result = new List>(); foreach (var r in rows.Skip(1)) { if (r.All(string.IsNullOrWhiteSpace)) continue; var d = new Dictionary(StringComparer.OrdinalIgnoreCase); for (var i = 0; i < header.Count; i++) d[header[i]] = i < r.Count ? r[i].Trim() : ""; result.Add(d); } return result; } private static List> ParseRows(string text) { var rows = new List>(); var row = new List(); var field = new StringBuilder(); var inQuotes = false; for (var i = 0; i < text.Length; i++) { var c = text[i]; if (inQuotes) { if (c == '"' && i + 1 < text.Length && text[i + 1] == '"') { field.Append('"'); i++; } else if (c == '"') inQuotes = false; else field.Append(c); continue; } switch (c) { case '"': inQuotes = true; break; case ',': row.Add(field.ToString()); field.Clear(); break; case '\r': break; case '\n': row.Add(field.ToString()); field.Clear(); rows.Add(row); row = []; break; default: field.Append(c); break; } } if (field.Length > 0 || row.Count > 0) { row.Add(field.ToString()); rows.Add(row); } return rows; } public static bool Bool(string? v, bool fallback = false) => string.IsNullOrWhiteSpace(v) ? fallback : v.Trim().ToLowerInvariant() is "1" or "y" or "yes" or "true"; public static string? NullIfEmpty(string? v) => string.IsNullOrWhiteSpace(v) ? null : v.Trim(); }