Fix ingredient parsing to be less obnoxious

This commit is contained in:
Matteo Settenvini 2022-08-12 23:36:27 +02:00
parent 7025ee9e3a
commit 63860cd05a
Signed by: matteo
GPG Key ID: 8576CC1AD97D42DF
2 changed files with 28 additions and 18 deletions

View File

@ -131,10 +131,7 @@ fn prepare_grocery_list(ingredients: &Vec<(Ingredient, Vec<String>)>) -> Result<
)?; )?;
writeln!(out)?; // leave an empty line writeln!(out)?; // leave an empty line
for (ingredient, recipes) in ingredients { for (ingredient, recipes) in ingredients {
writeln!(out, "- [ ] {}", ingredient)?; writeln!(out, "- [ ] {} ({})", ingredient, recipes.join(", "))?;
for recipe in recipes {
writeln!(out, " * {}", recipe)?;
}
} }
Ok(out) Ok(out)

View File

@ -159,7 +159,7 @@ impl<'de> serde::de::Visitor<'de> for IngredientVisitor {
where where
E: serde::de::Error, E: serde::de::Error,
{ {
use std::cmp::min; use std::cmp::{max, min};
let quantity_regex_start: regex::Regex = let quantity_regex_start: regex::Regex =
regex::Regex::new(r"^(?P<v>[0-9,\.]+)\s*(?P<u>\w+)?\s+(?P<i>.*)").unwrap(); regex::Regex::new(r"^(?P<v>[0-9,\.]+)\s*(?P<u>\w+)?\s+(?P<i>.*)").unwrap();
@ -170,20 +170,26 @@ impl<'de> serde::de::Visitor<'de> for IngredientVisitor {
.captures(value) .captures(value)
.or_else(|| quantity_regex_end.captures(value)) .or_else(|| quantity_regex_end.captures(value))
{ {
let v = captures.name("v").unwrap().as_str(); let vm = captures.name("v").unwrap();
let v = vm.as_str();
let um = captures.name("u"); let um = captures.name("u");
let u = um.map(|u| u.as_str()).unwrap_or(""); let u = um.map(|u| u.as_str()).unwrap_or("");
let im = captures.name("i").unwrap(); let im = captures.name("i").unwrap();
let i = im.as_str(); let i = im.as_str();
let (amount, unit) = Self::parse_quantity(v, u)?; let (mut amount, unit) = Self::parse_quantity(v, u)?;
let name = match unit { let name = match unit {
Unit::None => { Unit::None => {
// pick the longest string including what was recognized by the regex as the possible unit // pick the longest string including what was recognized by the regex as the possible unit
let start = um let range = um
.map(|um| min(um.start(), im.start())) .map(|um| min(um.start(), im.start())..max(um.end(), im.end()))
.unwrap_or_else(|| im.start()); .unwrap_or_else(|| im.start()..im.end());
&value[start..im.end()]
if range.start <= vm.start() && range.end >= vm.end() {
amount = 1.0; // e.g. for "Parsley, 5 leaves" -> Ingredient { 1, no unit, 'Parsley, 5 leaves' }
}
&value[range]
} }
_ => i, _ => i,
} }
@ -218,7 +224,7 @@ impl IngredientVisitor {
v = v / 1000.0; v = v / 1000.0;
Unit::Kilogram Unit::Kilogram
} }
"hg" => { "hg" | "etto" | "etti" => {
v = v / 10.0; v = v / 10.0;
Unit::Kilogram Unit::Kilogram
} }
@ -231,15 +237,15 @@ impl IngredientVisitor {
v = v / 100.0; v = v / 100.0;
Unit::Liter Unit::Liter
} }
"ml" => { "ml" | "cc" => {
v = v / 1000.0; v = v / 1000.0;
Unit::Liter Unit::Liter
} }
"tl" => { "tl" | "cucchiaino" | "cucchiaini" => {
v = v / 1000.0 * 5.0; v = v / 1000.0 * 5.0;
Unit::Liter Unit::Liter
} }
"el" => { "el" | "cucchiaio" | "cucchiai" => {
v = v / 1000.0 * 15.0; v = v / 1000.0 * 15.0;
Unit::Liter Unit::Liter
} }
@ -260,12 +266,19 @@ impl IngredientVisitor {
impl core::fmt::Display for Ingredient { impl core::fmt::Display for Ingredient {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
const PRECISION: u32 = 3;
const ROUNDING_EXP: f64 = 10isize.pow(PRECISION) as f64;
write!( write!(
f, f,
"{amount:.3}{unit} {name}", "{amount}{pad}{unit} {name}",
name = self.name, name = self.name,
amount = self.amount, amount = (self.amount * ROUNDING_EXP).round() / ROUNDING_EXP,
unit = self.unit pad = match self.unit {
Unit::None => "",
_ => " ",
},
unit = self.unit,
) )
} }
} }