kabouzeid / recipe-ingredient-parser

Recipe Ingredient Parser

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

🍜 Recipe Ingredient Parser

Uses a parsing expression grammar and pest to decompose a recipe ingredient string into its amount, unit and ingredient parts.

Examples

Screenshot 2022-11-16 at 14 59 53

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse() {
{
let info = parse("1 1/2 kg potatoes");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Constant {
value: Constant::Fraction(3, 2)
}
);
assert_eq!(amount.span.from, 0);
assert_eq!(amount.span.to, 5);
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Kilogram);
assert_eq!(unit.span.from, 6);
assert_eq!(unit.span.to, 8);
}
{
let info = parse("2-3 lb potatoes");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Range {
value_from: Constant::Fraction(2, 1),
value_to: Constant::Fraction(3, 1)
}
);
assert_eq!(amount.span.from, 0);
assert_eq!(amount.span.to, 3);
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Pound);
assert_eq!(unit.span.from, 4);
assert_eq!(unit.span.to, 6);
}
{
let info = parse("1½ cups flour");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Constant {
value: Constant::Fraction(3, 2)
}
);
assert_eq!(amount.span.from, 0);
assert_eq!(amount.span.to, 3); // because ½ takes two bytes
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Cup);
assert_eq!(unit.span.from, 4);
assert_eq!(unit.span.to, 8);
}
{
let info = parse("400 ml milk");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Constant {
value: Constant::Fraction(400, 1)
}
);
assert_eq!(amount.span.from, 0);
assert_eq!(amount.span.to, 3); // because ½ takes two bytes
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Milliliter);
assert_eq!(unit.span.from, 4);
assert_eq!(unit.span.to, 6);
}
}
#[test]
fn test_reverse_format() {
{
let info = parse("Flour 1 kg");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Constant {
value: Constant::Fraction(1, 1)
}
);
assert_eq!(amount.span.from, 6);
assert_eq!(amount.span.to, 7); // because ½ takes two bytes
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Kilogram);
assert_eq!(unit.span.from, 8);
assert_eq!(unit.span.to, 10);
}
{
// (2 kg) is not recognized as container size in reverse
let info = parse("Flour (2 kg) 1 kg");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Constant {
value: Constant::Fraction(1, 1)
}
);
assert_eq!(amount.span.from, 13);
assert_eq!(amount.span.to, 14); // because ½ takes two bytes
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Kilogram);
assert_eq!(unit.span.from, 15);
assert_eq!(unit.span.to, 17);
}
}
#[test]
fn test_unit_case_insensitive() {
{
let info = parse("1 1/2 KG potatoes");
let amount = info.amount.unwrap();
assert_eq!(
amount.value,
Amount::Constant {
value: Constant::Fraction(3, 2)
}
);
assert_eq!(amount.span.from, 0);
assert_eq!(amount.span.to, 5);
let unit = info.unit.unwrap();
assert_eq!(unit.value, Unit::Kilogram);
assert_eq!(unit.span.from, 6);
assert_eq!(unit.span.to, 8);
}
}
#[test]
fn test_space_between_unit_and_ingredient() {
// not "1 L ettuce"
let info = parse("1 lettuce");
assert!(info.amount.is_some());
assert!(info.unit.is_none());
// not "8 t omatoes"
let info = parse("8 tomatoes");
assert!(info.amount.is_some());
assert!(info.unit.is_none());
// not "olive oi L"
let info = parse("olive oil");
assert!(info.amount.is_none());
assert!(info.unit.is_none());
}
#[test]
fn test_simple_fractions() {
let value = Amount::Constant {
value: Constant::Fraction(2, 3),
};
assert_eq!(parse("2/3").amount.unwrap().value, value);
assert_eq!(parse("2 /3").amount.unwrap().value, value);
assert_eq!(parse("2/ 3").amount.unwrap().value, value);
assert_eq!(parse("2 / 3").amount.unwrap().value, value);
}
#[test]
fn test_compound_fractions() {
let value = Amount::Constant {
value: Constant::Fraction(5, 3),
};
assert_eq!(parse("1 2/3").amount.unwrap().value, value);
assert_eq!(parse("1 2 /3").amount.unwrap().value, value);
assert_eq!(parse("1 2/ 3").amount.unwrap().value, value);
assert_eq!(parse("1 2 / 3").amount.unwrap().value, value);
}
#[test]
fn test_ingredient_only() {
// not "1 L ettuce"
let info = parse("lettuce");
assert!(info.amount.is_none());
assert!(info.unit.is_none());
}
#[test]
fn test_space_between_word_digit_and_unit() {
// not "a L milk"
let info = parse("Al milk");
assert!(info.unit.is_none());
assert!(info.amount.is_none());
}
#[test]
fn test_amount_only() {
let info = parse("1");
assert!(info.amount.is_some());
let info = parse("1/2 - 2/3");
assert!(info.amount.is_some());
let info = parse(".3");
assert!(info.amount.is_some());
let info = parse("one");
assert!(info.amount.is_some());
}
#[test]
fn test_unit_only() {
let info = parse("kg");
assert_eq!(info.unit.unwrap().value, Unit::Kilogram);
let info = parse("handful");
assert_eq!(info.unit.unwrap().value, Unit::Handful);
}
#[test]
fn test_quantity_only() {
let info = parse("1 kg");
println!("INFO: {:?}", info);
assert!(info.amount.is_some());
assert!(info.unit.is_some());
let info = parse(" 2 g ");
assert!(info.amount.is_some());
assert!(info.unit.is_some());
}
#[test]
fn test_space_in_unit() {
let info = parse("salt & pepper to taste");
assert_eq!(info.unit.unwrap().value, Unit::ToTaste);
}
#[test]
fn test_expr_to_int() {
assert_eq!(expr_to_int("a").unwrap(), 1);
assert_eq!(expr_to_int("two").unwrap(), 2);
}
#[test]
fn test_expr_to_unit() {
assert_eq!(expr_to_unit("kilogram").unwrap(), Unit::Kilogram);
assert_eq!(expr_to_unit("lb").unwrap(), Unit::Pound);
// assert_eq!(expr_to_unit("t").unwrap(), Unit::Teaspoon);
// assert_eq!(expr_to_unit("T").unwrap(), Unit::Tablespoon);
assert_eq!(expr_to_unit("ml.").unwrap(), Unit::Milliliter);
}
}

Web Usage

Ready for web usage with wasm-pack.

About

Recipe Ingredient Parser

License:MIT License


Languages

Language:Rust 100.0%