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();
}