mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
Have a packaging unit for measures
This commit is contained in:
parent
94e1987f09
commit
9022503e76
@ -156,12 +156,20 @@ impl IngredientAccumulator {
|
|||||||
set.insert(recipe_title.clone());
|
set.insert(recipe_title.clone());
|
||||||
self.inner.insert(key, (i.clone(), set));
|
self.inner.insert(key, (i.clone(), set));
|
||||||
} else {
|
} else {
|
||||||
let amt = match (self.inner[&key].0.amt, i.amt) {
|
let amts = match (&self.inner[&key].0.amt, &i.amt) {
|
||||||
(Volume(rvm), Volume(lvm)) => Volume(lvm + rvm),
|
(Volume(rvm), Volume(lvm)) => vec![Volume(lvm + rvm)],
|
||||||
(Count(lqty), Count(rqty)) => Count(lqty + rqty),
|
(Count(lqty), Count(rqty)) => vec![Count(lqty + rqty)],
|
||||||
(Weight(lqty), Weight(rqty)) => Weight(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!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
for amt in amts {
|
||||||
self.inner.get_mut(&key).map(|(i, set)| {
|
self.inner.get_mut(&key).map(|(i, set)| {
|
||||||
i.amt = amt;
|
i.amt = amt;
|
||||||
set.insert(recipe_title.clone());
|
set.insert(recipe_title.clone());
|
||||||
@ -169,6 +177,7 @@ impl IngredientAccumulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn accumulate_from(&mut self, r: &Recipe) {
|
pub fn accumulate_from(&mut self, r: &Recipe) {
|
||||||
self.accumulate_ingredients_for(
|
self.accumulate_ingredients_for(
|
||||||
|
@ -334,7 +334,14 @@ make_fn!(unit<StrIter, String>,
|
|||||||
text_token!("kg"),
|
text_token!("kg"),
|
||||||
text_token!("grams"),
|
text_token!("grams"),
|
||||||
text_token!("gram"),
|
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,
|
_ => ws,
|
||||||
(u.to_lowercase().to_singular())
|
(u.to_lowercase().to_singular())
|
||||||
)
|
)
|
||||||
@ -393,6 +400,7 @@ pub fn measure(i: StrIter) -> abortable_parser::Result<StrIter, Measure> {
|
|||||||
"oz" => Weight(Oz(qty)),
|
"oz" => Weight(Oz(qty)),
|
||||||
"kg" | "kilogram" => Weight(Kilogram(qty)),
|
"kg" | "kilogram" => Weight(Kilogram(qty)),
|
||||||
"g" | "gram" => Weight(Gram(qty)),
|
"g" | "gram" => Weight(Gram(qty)),
|
||||||
|
"pkg" | "package" | "can" | "bag" | "bottle" | "bot" => Measure::pkg(s, qty),
|
||||||
_u => {
|
_u => {
|
||||||
eprintln!("Invalid unit: {}", _u);
|
eprintln!("Invalid unit: {}", _u);
|
||||||
unreachable!()
|
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 {
|
pub fn normalize_name(name: &str) -> String {
|
||||||
let parts: Vec<&str> = name.split_whitespace().collect();
|
let parts: Vec<&str> = name.split_whitespace().collect();
|
||||||
if parts.len() >= 2 {
|
if parts.len() >= 2 {
|
||||||
|
@ -235,32 +235,30 @@ fn test_ingredient_name_parse() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ingredient_parse() {
|
fn test_ingredient_parse() {
|
||||||
for (i, expected) in vec![
|
for (i, expected) in vec![
|
||||||
//(
|
(
|
||||||
// "1 cup flour ",
|
"1 cup flour ",
|
||||||
// Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1))), ""),
|
Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1)))),
|
||||||
//),
|
),
|
||||||
//(
|
(
|
||||||
// "\t1 cup flour ",
|
"\t1 cup flour ",
|
||||||
// Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1))), ""),
|
Ingredient::new("flour", None, Volume(Cup(Quantity::Whole(1)))),
|
||||||
//),
|
),
|
||||||
//(
|
(
|
||||||
// "1 cup apple (chopped)",
|
"1 cup apple (chopped)",
|
||||||
// Ingredient::new(
|
Ingredient::new(
|
||||||
// "apple",
|
"apple",
|
||||||
// Some("chopped".to_owned()),
|
Some("chopped".to_owned()),
|
||||||
// Volume(Cup(Quantity::Whole(1))),
|
Volume(Cup(Quantity::Whole(1))),
|
||||||
// "",
|
),
|
||||||
// ),
|
),
|
||||||
//),
|
(
|
||||||
//(
|
"1 cup apple (chopped) ",
|
||||||
// "1 cup apple (chopped) ",
|
Ingredient::new(
|
||||||
// Ingredient::new(
|
"apple",
|
||||||
// "apple",
|
Some("chopped".to_owned()),
|
||||||
// Some("chopped".to_owned()),
|
Volume(Cup(Quantity::Whole(1))),
|
||||||
// Volume(Cup(Quantity::Whole(1))),
|
),
|
||||||
// "",
|
),
|
||||||
// ),
|
|
||||||
//),
|
|
||||||
(
|
(
|
||||||
"1 green bell pepper (chopped) ",
|
"1 green bell pepper (chopped) ",
|
||||||
Ingredient::new(
|
Ingredient::new(
|
||||||
@ -269,6 +267,46 @@ fn test_ingredient_parse() {
|
|||||||
Count(Quantity::Whole(1)),
|
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)) {
|
match parse::ingredient(StrIter::new(i)) {
|
||||||
ParseResult::Complete(_, ing) => assert_eq!(ing, expected),
|
ParseResult::Complete(_, ing) => assert_eq!(ing, expected),
|
||||||
|
@ -21,7 +21,7 @@ use std::{
|
|||||||
cmp::{Ordering, PartialEq, PartialOrd},
|
cmp::{Ordering, PartialEq, PartialOrd},
|
||||||
convert::TryFrom,
|
convert::TryFrom,
|
||||||
fmt::Display,
|
fmt::Display,
|
||||||
ops::{Add, Div, Mul, Sub},
|
ops::{Add, Div, Mul, Sub}, rc::Rc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use num_rational::Ratio;
|
use num_rational::Ratio;
|
||||||
@ -179,6 +179,20 @@ impl VolumeMeasure {
|
|||||||
|
|
||||||
macro_rules! volume_op {
|
macro_rules! volume_op {
|
||||||
($trait:ident, $method:ident) => {
|
($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 {
|
impl $trait for VolumeMeasure {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -293,6 +307,20 @@ impl WeightMeasure {
|
|||||||
|
|
||||||
macro_rules! weight_op {
|
macro_rules! weight_op {
|
||||||
($trait:ident, $method:ident) => {
|
($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 {
|
impl $trait for WeightMeasure {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -335,18 +363,19 @@ impl Display for WeightMeasure {
|
|||||||
|
|
||||||
use WeightMeasure::{Gram, Kilogram, Oz, Pound};
|
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.
|
/// Measurements in a Recipe with associated units for them.
|
||||||
pub enum Measure {
|
pub enum Measure {
|
||||||
/// Volume measurements as meter cubed base unit
|
/// Volume measurements as meter cubed base unit
|
||||||
Volume(VolumeMeasure),
|
Volume(VolumeMeasure),
|
||||||
/// Simple count of items
|
/// Simple count of items
|
||||||
Count(Quantity),
|
Count(Quantity),
|
||||||
|
Package(Rc<str>, Quantity),
|
||||||
/// Weight measure as Grams base unit
|
/// Weight measure as Grams base unit
|
||||||
Weight(WeightMeasure),
|
Weight(WeightMeasure),
|
||||||
}
|
}
|
||||||
|
|
||||||
use Measure::{Count, Volume, Weight};
|
use Measure::{Count, Volume, Weight, Package};
|
||||||
|
|
||||||
impl Measure {
|
impl Measure {
|
||||||
pub fn tsp(qty: Quantity) -> Self {
|
pub fn tsp(qty: Quantity) -> Self {
|
||||||
@ -407,11 +436,16 @@ impl Measure {
|
|||||||
Weight(Oz(qty))
|
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 {
|
pub fn measure_type(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Volume(_) => "Volume",
|
Volume(_) => "Volume",
|
||||||
Count(_) => "Count",
|
Count(_) => "Count",
|
||||||
Weight(_) => "Weight",
|
Weight(_) => "Weight",
|
||||||
|
Package(_, _) => "Package",
|
||||||
}
|
}
|
||||||
.to_owned()
|
.to_owned()
|
||||||
}
|
}
|
||||||
@ -421,6 +455,7 @@ impl Measure {
|
|||||||
Volume(vm) => vm.plural(),
|
Volume(vm) => vm.plural(),
|
||||||
Count(qty) => qty.plural(),
|
Count(qty) => qty.plural(),
|
||||||
Weight(wm) => wm.plural(),
|
Weight(wm) => wm.plural(),
|
||||||
|
Package(_, qty) => qty.plural(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,6 +464,7 @@ impl Measure {
|
|||||||
Volume(vm) => Volume(vm.normalize()),
|
Volume(vm) => Volume(vm.normalize()),
|
||||||
Count(qty) => Count(qty.clone()),
|
Count(qty) => Count(qty.clone()),
|
||||||
Weight(wm) => Weight(wm.normalize()),
|
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),
|
Volume(vm) => write!(w, "{}", vm),
|
||||||
Count(qty) => write!(w, "{}", qty),
|
Count(qty) => write!(w, "{}", qty),
|
||||||
Weight(wm) => write!(w, "{}", wm),
|
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 {
|
macro_rules! quantity_op {
|
||||||
($trait:ident, $method:ident) => {
|
($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 {
|
impl $trait for Quantity {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user