import "server-only"; // Minimaler RFC-4180-artiger CSV-Parser/-Serializer für Import/Export der // Gerichte-/Allergen-/Zusatzstofflisten. Bewusst ohne npm-Abhängigkeit // (Projekt-Stil: kein ORM, keine Validierungs-Library, minimale Deps) – // deckt quoted Felder mit eingebetteten Kommas/Anführungszeichen/ // Zeilenumbrüchen ab, da Gerichtsnamen Kommas enthalten können. export function parseCsv(text: string): string[][] { const rows: string[][] = []; let row: string[] = []; let field = ""; let inQuotes = false; let i = 0; const len = text.length; function pushField() { row.push(field); field = ""; } function pushRow() { pushField(); rows.push(row); row = []; } while (i < len) { const c = text[i]; if (inQuotes) { if (c === '"') { if (text[i + 1] === '"') { field += '"'; i += 2; continue; } inQuotes = false; i++; continue; } field += c; i++; continue; } if (c === '"') { inQuotes = true; i++; continue; } if (c === ",") { pushField(); i++; continue; } if (c === "\r") { i++; continue; } if (c === "\n") { pushRow(); i++; continue; } field += c; i++; } // Letzte Zeile ohne abschließenden Zeilenumbruch noch anhängen. if (field !== "" || row.length > 0) pushRow(); // Leere Zeilen überspringen (z. B. durch einen abschließenden Zeilenumbruch). return rows.filter((r) => !(r.length === 1 && r[0].trim() === "")); } function csvField(value: string): string { if (/[",\r\n]/.test(value)) { return `"${value.replace(/"/g, '""')}"`; } return value; } export function toCsv(rows: string[][]): string { return rows.map((row) => row.map(csvField).join(",")).join("\r\n") + "\r\n"; } /** Kopfzeile in Kleinbuchstaben, damit der Import tolerant gegenüber Groß-/ * Kleinschreibung und Spaltenreihenfolge ist. */ export function parseCsvWithHeader(text: string): { header: string[]; rows: string[][] } { const table = parseCsv(text); if (table.length === 0) return { header: [], rows: [] }; const [header, ...rows] = table; return { header: header.map((h) => h.trim().toLowerCase()), rows }; } /** Codes einer Zelle wie "A;G;2" in einzelne, getrimmte Kürzel zerlegen. */ export function splitCodes(cell: string | undefined): string[] { return (cell ?? "") .split(";") .map((c) => c.trim()) .filter(Boolean); }