Files
Cibello-app/packages/nutrition-engine/src/units.ts
T
2026-08-05 19:21:11 +07:00

71 lines
2.2 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 { UNIT_INFO, type Unit, type UnitKind } from "@app/shared-types";
export interface IngredientUnitInfo {
/** g per ml krävs för volym → massa. */
densityGPerMl?: number | null;
/** g per styck krävs för antal → massa. */
gramsPerPiece?: number | null;
}
export function unitKind(unit: Unit): UnitKind {
return UNIT_INFO[unit].kind;
}
/** Konvertera till basenhet inom samma slag (g, ml eller st). */
export function toBase(quantity: number, unit: Unit): { kind: UnitKind; amount: number } {
const info = UNIT_INFO[unit];
return { kind: info.kind, amount: quantity * info.toBase };
}
/**
* Konvertera valfri mängd till gram. Returnerar null när konvertering inte är
* möjlig utan mer information hellre ärlig osäkerhet än gissning (spec §61.4).
*/
export function toGrams(
quantity: number,
unit: Unit,
info: IngredientUnitInfo = {},
): number | null {
const base = toBase(quantity, unit);
switch (base.kind) {
case "mass":
return base.amount;
case "volume": {
const density = info.densityGPerMl;
if (density == null || density <= 0) return null;
return base.amount * density;
}
case "count": {
const perPiece = info.gramsPerPiece;
if (perPiece == null || perPiece <= 0) return null;
return base.amount * perPiece;
}
}
}
/** Konvertera en mängd mellan enheter (samma slag, eller via densitet/styckvikt). */
export function convert(
quantity: number,
from: Unit,
to: Unit,
info: IngredientUnitInfo = {},
): number | null {
const fromInfo = UNIT_INFO[from];
const toInfo = UNIT_INFO[to];
if (fromInfo.kind === toInfo.kind) {
return (quantity * fromInfo.toBase) / toInfo.toBase;
}
// Olika slag: gå via gram.
const grams = toGrams(quantity, from, info);
if (grams == null) return null;
if (toInfo.kind === "mass") return grams / toInfo.toBase;
if (toInfo.kind === "volume") {
const density = info.densityGPerMl;
if (density == null || density <= 0) return null;
return grams / density / toInfo.toBase;
}
const perPiece = info.gramsPerPiece;
if (perPiece == null || perPiece <= 0) return null;
return grams / perPiece / toInfo.toBase;
}