Files
kochwas/src/lib/server/parsers/ingredient.ts
Hendrik ae774e377d feat(parser): add ingredient parser
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-17 15:02:09 +02:00

69 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Ingredient } from '$lib/types';
const UNITS = new Set([
'g',
'kg',
'ml',
'l',
'cl',
'dl',
'TL',
'EL',
'Prise',
'Pck.',
'Pkg',
'Becher',
'Stk',
'Stück',
'Bund',
'Tasse',
'Dose'
]);
const FRACTION_MAP: Record<string, number> = {
'1/2': 0.5,
'1/3': 1 / 3,
'2/3': 2 / 3,
'1/4': 0.25,
'3/4': 0.75
};
function parseQuantity(raw: string): number | null {
const trimmed = raw.trim();
if (FRACTION_MAP[trimmed] !== undefined) return FRACTION_MAP[trimmed];
const rangeMatch = /^(\d+[.,]?\d*)\s*[-]\s*\d+[.,]?\d*$/.exec(trimmed);
if (rangeMatch) {
return parseFloat(rangeMatch[1].replace(',', '.'));
}
const num = parseFloat(trimmed.replace(',', '.'));
return Number.isFinite(num) ? num : null;
}
export function parseIngredient(raw: string, position = 0): Ingredient {
const rawText = raw.trim();
let working = rawText;
let note: string | null = null;
const noteMatch = /\(([^)]+)\)/.exec(working);
if (noteMatch) {
note = noteMatch[1].trim();
working = (
working.slice(0, noteMatch.index) + working.slice(noteMatch.index + noteMatch[0].length)
).trim();
}
const qtyPattern = /^((?:\d+[.,]?\d*(?:\s*[-]\s*\d+[.,]?\d*)?)|(?:\d+\/\d+))\s+(.+)$/;
const qtyMatch = qtyPattern.exec(working);
if (!qtyMatch) {
return { position, quantity: null, unit: null, name: working, note, raw_text: rawText };
}
const quantity = parseQuantity(qtyMatch[1]);
let rest = qtyMatch[2].trim();
let unit: string | null = null;
const firstTokenMatch = /^(\S+)\s+(.+)$/.exec(rest);
if (firstTokenMatch && UNITS.has(firstTokenMatch[1])) {
unit = firstTokenMatch[1];
rest = firstTokenMatch[2].trim();
}
return { position, quantity, unit, name: rest, note, raw_text: rawText };
}