Have a packaging unit for measures

This commit is contained in:
Jeremy Wall 2024-01-03 14:02:48 -05:00
parent 94e1987f09
commit 9022503e76
4 changed files with 148 additions and 38 deletions

View File

@ -156,16 +156,25 @@ impl IngredientAccumulator {
set.insert(recipe_title.clone());
self.inner.insert(key, (i.clone(), set));
} else {
let amt = match (self.inner[&key].0.amt, i.amt) {
(Volume(rvm), Volume(lvm)) => Volume(lvm + rvm),
(Count(lqty), Count(rqty)) => Count(lqty + rqty),
(Weight(lqty), Weight(rqty)) => Weight(lqty + rqty),
let amts = match (&self.inner[&key].0.amt, &i.amt) {
(Volume(rvm), Volume(lvm)) => vec![Volume(lvm + rvm)],
(Count(lqty), Count(rqty)) => vec![Count(lqty + rqty)],
(Weight(lqty), Weight(rqty)) => vec![Weight(lqty + rqty)],
(Package(lnm, lqty), Package(rnm, rqty)) => {
if lnm == rnm {
vec![Package(lnm.clone(), lqty + rqty)]
} else {
vec![Package(lnm.clone(), lqty.clone()), Package(rnm.clone(), rqty.clone())]
}
}
_ => unreachable!(),
};
self.inner.get_mut(&key).map(|(i, set)| {
i.amt = amt;
set.insert(recipe_title.clone());
});
for amt in amts {
self.inner.get_mut(&key).map(|(i, set)| {
i.amt = amt;
set.insert(recipe_title.clone());
});
}
}
}
}

View File

@ -334,7 +334,14 @@ make_fn!(unit<StrIter, String>,
text_token!("kg"),
text_token!("grams"),
text_token!("gram"),
text_token!("g")),
text_token!("g"),
text_token!("pkg"),
text_token!("package"),
text_token!("bottle"),
text_token!("bot"),
text_token!("bag"),
text_token!("can")
),
_ => ws,
(u.to_lowercase().to_singular())
)
@ -393,6 +400,7 @@ pub fn measure(i: StrIter) -> abortable_parser::Result<StrIter, Measure> {
"oz" => Weight(Oz(qty)),
"kg" | "kilogram" => Weight(Kilogram(qty)),
"g" | "gram" => Weight(Gram(qty)),
"pkg" | "package" | "can" | "bag" | "bottle" | "bot" => Measure::pkg(s, qty),
_u => {
eprintln!("Invalid unit: {}", _u);
unreachable!()
@ -411,6 +419,8 @@ pub fn measure(i: StrIter) -> abortable_parser::Result<StrIter, Measure> {
}
}
// TODO(jwall): I think this is a mistake. We should rethink what noralizing means or we should
// remove it.
pub fn normalize_name(name: &str) -> String {
let parts: Vec<&str> = name.split_whitespace().collect();
if parts.len() >= 2 {

View File

@ -235,32 +235,30 @@ fn test_ingredient_name_parse() {
#[test]
fn test_ingredient_parse() {
for (i, expected) in vec![
//(
// "1 cup flour ",
// Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1))), ""),
//),
//(
// "\t1 cup flour ",
// Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1))), ""),
//),
//(
// "1 cup apple (chopped)",
// Ingredient::new(
// "apple",
// Some("chopped".to_owned()),
// Volume(Cup(Quantity::Whole(1))),
// "",
// ),
//),
//(
// "1 cup apple (chopped) ",
// Ingredient::new(
// "apple",
// Some("chopped".to_owned()),
// Volume(Cup(Quantity::Whole(1))),
// "",
// ),
//),
(
"1 cup flour ",
Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1)))),
),
(
"\t1 cup flour ",
Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1)))),
),
(
"1 cup apple (chopped)",
Ingredient::new(
"apple",
Some("chopped".to_owned()),
Volume(Cup(Quantity::Whole(1))),
),
),
(
"1 cup apple (chopped) ",
Ingredient::new(
"apple",
Some("chopped".to_owned()),
Volume(Cup(Quantity::Whole(1))),
),
),
(
"1 green bell pepper (chopped) ",
Ingredient::new(
@ -269,6 +267,46 @@ fn test_ingredient_parse() {
Count(Quantity::Whole(1)),
),
),
(
"1 pkg green onion",
Ingredient::new(
"green onion",
None,
Package("pkg".into(), Quantity::Whole(1)),
),
),
(
"1 bottle green onion",
Ingredient::new(
"green onion",
None,
Package("bottle".into(), Quantity::Whole(1)),
),
),
(
"1 bot green onion",
Ingredient::new(
"green onion",
None,
Package("bot".into(), Quantity::Whole(1)),
),
),
(
"1 bag green onion",
Ingredient::new(
"green onion",
None,
Package("bag".into(), Quantity::Whole(1)),
),
),
(
"1 can baked beans",
Ingredient::new(
"baked bean",
None,
Package("can".into(), Quantity::Whole(1)),
),
),
] {
match parse::ingredient(StrIter::new(i)) {
ParseResult::Complete(_, ing) => assert_eq!(ing, expected),

View File

@ -21,7 +21,7 @@ use std::{
cmp::{Ordering, PartialEq, PartialOrd},
convert::TryFrom,
fmt::Display,
ops::{Add, Div, Mul, Sub},
ops::{Add, Div, Mul, Sub}, rc::Rc,
};
use num_rational::Ratio;
@ -179,6 +179,20 @@ impl VolumeMeasure {
macro_rules! volume_op {
($trait:ident, $method:ident) => {
impl $trait for &VolumeMeasure {
type Output = VolumeMeasure;
fn $method(self, lhs: Self) -> Self::Output {
let (l, r) = (self.get_ml(), lhs.get_ml());
let result = ML($trait::$method(l, r));
if self.metric() {
result.normalize()
} else {
result.into_tsp().normalize()
}
}
}
impl $trait for VolumeMeasure {
type Output = Self;
@ -293,6 +307,20 @@ impl WeightMeasure {
macro_rules! weight_op {
($trait:ident, $method:ident) => {
impl $trait for &WeightMeasure {
type Output = WeightMeasure;
fn $method(self, lhs: Self) -> Self::Output {
let (l, r) = (self.get_grams(), lhs.get_grams());
let result = WeightMeasure::Gram($trait::$method(l, r));
if self.metric() {
result.normalize()
} else {
result.into_oz().normalize()
}
}
}
impl $trait for WeightMeasure {
type Output = Self;
@ -335,18 +363,19 @@ impl Display for WeightMeasure {
use WeightMeasure::{Gram, Kilogram, Oz, Pound};
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord)]
/// Measurements in a Recipe with associated units for them.
pub enum Measure {
/// Volume measurements as meter cubed base unit
Volume(VolumeMeasure),
/// Simple count of items
Count(Quantity),
Package(Rc<str>, Quantity),
/// Weight measure as Grams base unit
Weight(WeightMeasure),
}
use Measure::{Count, Volume, Weight};
use Measure::{Count, Volume, Weight, Package};
impl Measure {
pub fn tsp(qty: Quantity) -> Self {
@ -407,11 +436,16 @@ impl Measure {
Weight(Oz(qty))
}
pub fn pkg<S: Into<Rc<str>>>(name: S, qty: Quantity) -> Self {
Package(name.into(), qty)
}
pub fn measure_type(&self) -> String {
match self {
Volume(_) => "Volume",
Count(_) => "Count",
Weight(_) => "Weight",
Package(_, _) => "Package",
}
.to_owned()
}
@ -421,6 +455,7 @@ impl Measure {
Volume(vm) => vm.plural(),
Count(qty) => qty.plural(),
Weight(wm) => wm.plural(),
Package(_, qty) => qty.plural(),
}
}
@ -429,6 +464,7 @@ impl Measure {
Volume(vm) => Volume(vm.normalize()),
Count(qty) => Count(qty.clone()),
Weight(wm) => Weight(wm.normalize()),
Package(nm, qty) => Package(nm.clone(), qty.clone()),
}
}
}
@ -439,6 +475,7 @@ impl Display for Measure {
Volume(vm) => write!(w, "{}", vm),
Count(qty) => write!(w, "{}", qty),
Weight(wm) => write!(w, "{}", wm),
Package(nm, qty) => write!(w, "{} {}", qty, nm),
}
}
}
@ -533,6 +570,22 @@ impl TryFrom<f32> for Quantity {
macro_rules! quantity_op {
($trait:ident, $method:ident) => {
impl $trait for &Quantity {
type Output = Quantity;
fn $method(self, lhs: Self) -> Self::Output {
match (self, lhs) {
(Whole(rhs), Whole(lhs)) => Frac($trait::$method(
Ratio::from_integer(*rhs),
Ratio::from_integer(*lhs),
)),
(Frac(rhs), Frac(lhs)) => Frac($trait::$method(rhs, lhs)),
(Whole(rhs), Frac(lhs)) => Frac($trait::$method(Ratio::from_integer(*rhs), lhs)),
(Frac(rhs), Whole(lhs)) => Frac($trait::$method(rhs, Ratio::from_integer(*lhs))),
}
}
}
impl $trait for Quantity {
type Output = Self;