diff --git a/api/src/domain/ingredient/fetch_list.rs b/api/src/domain/ingredient/fetch_list.rs index dbd0164..c5a4c40 100644 --- a/api/src/domain/ingredient/fetch_list.rs +++ b/api/src/domain/ingredient/fetch_list.rs @@ -1,5 +1,4 @@ -use super::types; -use crate::domain::misc_types::Context; +use crate::domain::{Context, Ingredient}; use crate::repo::ingredient::IngredientRepo; #[derive(Default, Debug)] @@ -7,18 +6,15 @@ pub struct RequestOpts { pub keys: Option>, } -pub fn execute( - repo: &impl IngredientRepo, - ctx: &Context, - opts: RequestOpts, -) -> Vec { +pub fn execute(repo: &impl IngredientRepo, ctx: &Context, opts: RequestOpts) -> Vec { repo.get_ingredients(ctx, opts.into()) } #[cfg(test)] mod tests { use super::*; - use crate::{domain::misc_types::Lang, repo::ingredient::InMemoryIngredientRepo}; + use crate::domain::Lang; + use crate::repo::ingredient::InMemoryIngredientRepo; #[test] fn should_return_all_ingredients() { diff --git a/api/src/domain/ingredient/types.rs b/api/src/domain/ingredient/types.rs index e3e6103..b94f16e 100644 --- a/api/src/domain/ingredient/types.rs +++ b/api/src/domain/ingredient/types.rs @@ -6,25 +6,3 @@ pub struct Ingredient { pub lang: Lang, pub name: String, } - -impl From<&db::data::Ingredient> for Ingredient { - fn from(db: &db::data::Ingredient) -> Self { - Self::from((db, Lang::Rus)) - } -} - -impl From<(&db::data::Ingredient, Lang)> for Ingredient { - fn from((db, lang): (&db::data::Ingredient, Lang)) -> Self { - let tr = &db.translates; - let name = match lang { - Lang::Rus => tr.rus, - Lang::Eng => tr.eng.unwrap_or(tr.rus), - }; - - Self { - key: db.key.to_string(), - lang: if name == tr.rus { Lang::Rus } else { lang }, - name: name.to_string(), - } - } -} diff --git a/api/src/domain/mod.rs b/api/src/domain/mod.rs index 10a63a7..2bd40ef 100644 --- a/api/src/domain/mod.rs +++ b/api/src/domain/mod.rs @@ -2,4 +2,6 @@ pub mod ingredient; pub mod misc_types; pub mod recipe; +pub use ingredient::types::Ingredient; pub use misc_types::{Context, Lang}; +pub use recipe::types::{Measure, Recipe, RecipeIngredient}; diff --git a/api/src/domain/recipe/fetch_list.rs b/api/src/domain/recipe/fetch_list.rs index 5cffb4b..d44685e 100644 --- a/api/src/domain/recipe/fetch_list.rs +++ b/api/src/domain/recipe/fetch_list.rs @@ -10,7 +10,7 @@ pub fn execute(repo: &impl RecipeRepo, ctx: &Context) -> Vec { mod tests { use super::*; use crate::{ - domain::{recipe::types::RecipeIngredientMeasure, Lang}, + domain::{Lang, Measure}, repo::recipe::InMemoryRecipeRepo, }; @@ -36,11 +36,11 @@ mod tests { match salad.ingredients.as_slice() { [banana, apple, orange] => { assert_eq!(banana.ingredient.key, "banana"); - assert_eq!(banana.measure, RecipeIngredientMeasure::Gram(150)); + assert_eq!(banana.measure, Measure::Gram(150)); assert_eq!(apple.ingredient.key, "apple"); - assert_eq!(apple.measure, RecipeIngredientMeasure::Gram(150)); + assert_eq!(apple.measure, Measure::Gram(150)); assert_eq!(orange.ingredient.key, "orange"); - assert_eq!(orange.measure, RecipeIngredientMeasure::Gram(150)); + assert_eq!(orange.measure, Measure::Gram(150)); } _ => unreachable!(), } diff --git a/api/src/domain/recipe/types.rs b/api/src/domain/recipe/types.rs index 475a628..13fd71d 100644 --- a/api/src/domain/recipe/types.rs +++ b/api/src/domain/recipe/types.rs @@ -9,60 +9,17 @@ pub struct Recipe { pub ingredients: Vec, } -impl TryFrom<(&db::data::Recipe, Lang, Vec)> for Recipe { - type Error = (); - - fn try_from( - (db, lang, ingredients): (&db::data::Recipe, Lang, Vec), - ) -> Result { - let tr = &db.translates; - let ctr = match lang { - Lang::Rus => &tr.rus, - Lang::Eng => tr.eng.as_ref().unwrap_or(&tr.rus), - }; - - let ingredients = db - .ingredients - .iter() - .map(|sing| { - ingredients - .iter() - .find(|ing| sing.key == ing.key) - .map(|ing| RecipeIngredient { - ingredient: ing.clone(), - measure: sing.measure.into(), - }) - .ok_or(()) - }) - .collect::, _>>()?; - - let instructions = ctr.instructions.iter().copied().map(String::from).collect(); - - Ok(Self { - key: db.key.to_string(), - lang: if ctr.name == tr.rus.name { - Lang::Rus - } else { - lang - }, - name: ctr.name.to_string(), - instructions, - ingredients, - }) - } -} - #[derive(Debug, Clone, Serialize)] pub struct RecipeIngredient { #[serde(flatten)] pub ingredient: Ingredient, #[serde(flatten)] - pub measure: RecipeIngredientMeasure, + pub measure: Measure, } #[derive(Debug, Clone, PartialEq, Eq, Serialize)] #[serde(tag = "measure", content = "amount")] -pub enum RecipeIngredientMeasure { +pub enum Measure { #[serde(rename = "g")] Gram(u32), #[serde(rename = "kg")] @@ -72,17 +29,3 @@ pub enum RecipeIngredientMeasure { #[serde(rename = "l")] Liter(u32), } - -impl From for RecipeIngredientMeasure { - fn from(db: db::data::RecipeIngredientMeasure) -> Self { - use db::data::RecipeIngredientMeasure as DbRIM; - use RecipeIngredientMeasure as RIM; - - match db { - DbRIM::Gram(val) => RIM::Gram(val), - DbRIM::KiloGram(val) => RIM::KiloGram(val), - DbRIM::MilliLiter(val) => RIM::MilliLiter(val), - DbRIM::Liter(val) => RIM::Liter(val), - } - } -} diff --git a/api/src/repo/ingredient.rs b/api/src/repo/ingredient.rs index e4ccb14..2910dd7 100644 --- a/api/src/repo/ingredient.rs +++ b/api/src/repo/ingredient.rs @@ -1,16 +1,5 @@ -use crate::domain::ingredient::{fetch_by_key, fetch_list, types}; -use crate::domain::misc_types::{self, Context}; - -#[derive(Default)] -pub struct GetIngredientOpts { - pub lang: Option, -} - -impl From for GetIngredientOpts { - fn from(app: fetch_by_key::RequestOpts) -> Self { - Self { lang: app.lang } - } -} +use crate::domain::ingredient::fetch_list; +use crate::domain::{Context, Ingredient, Lang}; #[derive(Default)] pub struct GetIngredientsOpts { @@ -24,27 +13,27 @@ impl From for GetIngredientsOpts { } pub trait IngredientRepo { - fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option; + fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option; - fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec; + fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec; } pub struct StaticIngredientRepo; impl IngredientRepo for StaticIngredientRepo { - fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option { - db::INGREDIENTS + fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option { + db::data::INGREDIENTS .iter() .find(|ing| ing.key == &key) - .map(|ing| types::Ingredient::from((ing, ctx.lang))) + .map(|ing| Ingredient::from((ing, ctx.lang))) } - fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec { - let langs = [ctx.lang].repeat(db::INGREDIENTS.len()); - db::INGREDIENTS + fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec { + let langs = [ctx.lang].repeat(db::data::INGREDIENTS.len()); + db::data::INGREDIENTS .iter() .zip(langs) - .map(types::Ingredient::from) + .map(Ingredient::from) .filter(|ing| opts.keys.is_none() || opts.keys.as_ref().unwrap().contains(&ing.key)) .collect::>() } @@ -52,7 +41,27 @@ impl IngredientRepo for StaticIngredientRepo { #[cfg(test)] pub struct InMemoryIngredientRepo { - pub ingredients: Vec, + pub ingredients: Vec, +} + +#[cfg(test)] +impl IngredientRepo for InMemoryIngredientRepo { + fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option { + self.ingredients + .iter() + .find(|ing| ing.key == &key) + .map(|ing| Ingredient::from((ing, ctx.lang))) + } + + fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec { + let langs = [ctx.lang].repeat(self.ingredients.len()); + self.ingredients + .iter() + .zip(langs) + .map(Ingredient::from) + .filter(|ing| opts.keys.is_none() || opts.keys.as_ref().unwrap().contains(&ing.key)) + .collect::>() + } } #[cfg(test)] @@ -60,37 +69,37 @@ impl InMemoryIngredientRepo { pub fn new() -> Self { Self { ingredients: vec![ - db::data::Ingredient { + db::Ingredient { key: "banana", - translates: db::data::IngredientTranslate { + translates: db::IngredientTranslate { rus: "Банан", eng: Some("Banana"), }, }, - db::data::Ingredient { + db::Ingredient { key: "apple", - translates: db::data::IngredientTranslate { + translates: db::IngredientTranslate { rus: "Яблоко", eng: Some("Apple"), }, }, - db::data::Ingredient { + db::Ingredient { key: "orange", - translates: db::data::IngredientTranslate { + translates: db::IngredientTranslate { rus: "Апельсин", eng: None, }, }, - db::data::Ingredient { + db::Ingredient { key: "salt", - translates: db::data::IngredientTranslate { + translates: db::IngredientTranslate { rus: "Соль", eng: Some("Salt"), }, }, - db::data::Ingredient { + db::Ingredient { key: "sugar", - translates: db::data::IngredientTranslate { + translates: db::IngredientTranslate { rus: "Сахар", eng: None, }, @@ -100,22 +109,18 @@ impl InMemoryIngredientRepo { } } -#[cfg(test)] -impl IngredientRepo for InMemoryIngredientRepo { - fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option { - self.ingredients - .iter() - .find(|ing| ing.key == &key) - .map(|ing| types::Ingredient::from((ing, ctx.lang))) - } +impl From<(&db::Ingredient, Lang)> for Ingredient { + fn from((db, lang): (&db::Ingredient, Lang)) -> Self { + let tr = &db.translates; + let name = match lang { + Lang::Rus => tr.rus, + Lang::Eng => tr.eng.unwrap_or(tr.rus), + }; - fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec { - let langs = [ctx.lang].repeat(self.ingredients.len()); - self.ingredients - .iter() - .zip(langs) - .map(types::Ingredient::from) - .filter(|ing| opts.keys.is_none() || opts.keys.as_ref().unwrap().contains(&ing.key)) - .collect::>() + Self { + key: db.key.to_string(), + lang: if name == tr.rus { Lang::Rus } else { lang }, + name: name.to_string(), + } } } diff --git a/api/src/repo/recipe.rs b/api/src/repo/recipe.rs index 661d903..c72cae7 100644 --- a/api/src/repo/recipe.rs +++ b/api/src/repo/recipe.rs @@ -1,61 +1,68 @@ -use crate::domain::{recipe::types, Context}; -use crate::repo; - -use super::ingredient::IngredientRepo; +use crate::domain::{Context, Ingredient, Lang, Measure, Recipe, RecipeIngredient}; #[cfg(test)] -use db::data as Db; -#[cfg(test)] -use db::data::RecipeIngredientMeasure as DbRIM; +use crate::repo::ingredient::InMemoryIngredientRepo; +use crate::repo::ingredient::{IngredientRepo, StaticIngredientRepo}; pub trait RecipeRepo { - fn get_recipes(&self, ctx: &Context) -> Vec; + fn get_recipes(&self, ctx: &Context) -> Vec; } pub struct StaticRecipeRepo; impl RecipeRepo for StaticRecipeRepo { - fn get_recipes(&self, ctx: &Context) -> Vec { - let ings_repo = repo::ingredient::StaticIngredientRepo; + fn get_recipes(&self, ctx: &Context) -> Vec { + let ings_repo = StaticIngredientRepo; let ings = ings_repo.get_ingredients(ctx, Default::default()); - let langs = [ctx.lang].repeat(db::RECIPES.len()); - db::RECIPES + db::data::RECIPES .iter() - .zip(langs) - .filter_map(|(rec, lang)| types::Recipe::try_from((rec, lang, ings.clone())).ok()) + .filter_map(|rec| Recipe::try_from((rec, ctx.lang, ings.clone())).ok()) .collect() } } #[cfg(test)] pub struct InMemoryRecipeRepo { - pub recipes: Vec, + pub recipes: Vec, +} + +#[cfg(test)] +impl RecipeRepo for InMemoryRecipeRepo { + fn get_recipes(&self, ctx: &Context) -> Vec { + let ings_repo = InMemoryIngredientRepo::new(); + let ings = ings_repo.get_ingredients(ctx, Default::default()); + + self.recipes + .iter() + .filter_map(|rec| Recipe::try_from((rec, ctx.lang, ings.clone())).ok()) + .collect() + } } #[cfg(test)] impl InMemoryRecipeRepo { pub fn new() -> Self { Self { - recipes: vec![Db::Recipe { + recipes: vec![db::Recipe { key: "fruit_salad", steps: 0, ingredients: vec![ - Db::RecipeIngredient { + db::RecipeIngredient { key: "banana", - measure: DbRIM::Gram(150), + measure: db::Measure::Gram(150), }, - Db::RecipeIngredient { + db::RecipeIngredient { key: "apple", - measure: DbRIM::Gram(150), + measure: db::Measure::Gram(150), }, - Db::RecipeIngredient { + db::RecipeIngredient { key: "orange", - measure: DbRIM::Gram(150), + measure: db::Measure::Gram(150), }, ], - translates: Db::RecipeTranslates { - rus: Db::RecipeTranslate { + translates: db::RecipeTranslates { + rus: db::RecipeTranslate { name: "Фруктовый салат", instructions: vec![ "Нарезать бананы кружочками", @@ -70,25 +77,25 @@ impl InMemoryRecipeRepo { } pub fn with_no_ingredients_found(mut self) -> Self { - self.recipes.push(Db::Recipe { + self.recipes.push(db::Recipe { key: "no_ingredients_found", steps: 0, ingredients: vec![ - Db::RecipeIngredient { + db::RecipeIngredient { key: "salt", - measure: DbRIM::Gram(5), + measure: db::Measure::Gram(5), }, - Db::RecipeIngredient { + db::RecipeIngredient { key: "sugar", - measure: DbRIM::Gram(4), + measure: db::Measure::Gram(4), }, - Db::RecipeIngredient { + db::RecipeIngredient { key: "wheat_flour", - measure: DbRIM::Gram(500), + measure: db::Measure::Gram(500), }, ], - translates: Db::RecipeTranslates { - rus: Db::RecipeTranslate { + translates: db::RecipeTranslates { + rus: db::RecipeTranslate { name: "Не найдены ингредиенты", instructions: vec![], }, @@ -99,17 +106,56 @@ impl InMemoryRecipeRepo { } } -#[cfg(test)] -impl RecipeRepo for InMemoryRecipeRepo { - fn get_recipes(&self, ctx: &Context) -> Vec { - let ings_repo = repo::ingredient::InMemoryIngredientRepo::new(); - let ings = ings_repo.get_ingredients(ctx, Default::default()); +impl TryFrom<(&db::Recipe, Lang, Vec)> for Recipe { + type Error = (); - let langs = [ctx.lang].repeat(self.recipes.len()); - self.recipes + fn try_from( + (db, lang, ingredients): (&db::Recipe, Lang, Vec), + ) -> Result { + let tr = &db.translates; + let ctr = match lang { + Lang::Rus => &tr.rus, + Lang::Eng => tr.eng.as_ref().unwrap_or(&tr.rus), + }; + + let ingredients = db + .ingredients .iter() - .zip(langs) - .filter_map(|(rec, lang)| types::Recipe::try_from((rec, lang, ings.clone())).ok()) - .collect() + .map(|sing| { + ingredients + .iter() + .find(|ing| sing.key == ing.key) + .map(|ing| RecipeIngredient { + ingredient: ing.clone(), + measure: sing.measure.into(), + }) + .ok_or(()) + }) + .collect::, _>>()?; + + let instructions = ctr.instructions.iter().copied().map(String::from).collect(); + + Ok(Self { + key: db.key.to_string(), + lang: if ctr.name == tr.rus.name { + Lang::Rus + } else { + lang + }, + name: ctr.name.to_string(), + instructions, + ingredients, + }) + } +} + +impl From for Measure { + fn from(db: db::Measure) -> Self { + match db { + db::Measure::Gram(val) => Measure::Gram(val), + db::Measure::KiloGram(val) => Measure::KiloGram(val), + db::Measure::MilliLiter(val) => Measure::MilliLiter(val), + db::Measure::Liter(val) => Measure::Liter(val), + } } } diff --git a/db/build.rs b/db/build.rs index 9aff7bb..bd90e44 100644 --- a/db/build.rs +++ b/db/build.rs @@ -14,7 +14,8 @@ fn main() { fn gen_data_mod() -> Result<(), std::io::Error> { let file = fs::File::create("src/data.rs")?; let mut buf = BufWriter::new(file); - write_structs(&mut buf)?; + + writeln!(buf, "use crate::types::*;\n")?; println!("cargo:rerun-if-changed=data/ingredients/*.toml"); write_ingredients(&mut buf)?; println!("cargo:rerun-if-changed=data/recipes/*.toml"); @@ -22,61 +23,6 @@ fn gen_data_mod() -> Result<(), std::io::Error> { Ok(()) } -fn write_structs(file: &mut BufWriter) -> Result<(), std::io::Error> { - let structs = r#"#![allow(dead_code)] - -#[derive(Debug)] -pub struct Ingredient { - pub key: &'static str, - pub translates: IngredientTranslate, -} - -#[derive(Debug)] -pub struct IngredientTranslate { - pub rus: &'static str, - pub eng: Option<&'static str>, -} - -#[derive(Debug)] -pub struct Recipe { - pub key: &'static str, - pub steps: u8, - pub ingredients: Vec, - pub translates: RecipeTranslates, -} - -#[derive(Debug)] -pub struct RecipeIngredient { - pub key: &'static str, - pub measure: RecipeIngredientMeasure, -} - -#[derive(Debug, Clone, Copy)] -pub enum RecipeIngredientMeasure { - Gram(u32), - KiloGram(u32), - MilliLiter(u32), - Liter(u32), -} - -#[derive(Debug)] -pub struct RecipeTranslates { - pub rus: RecipeTranslate, - pub eng: Option, -} - -#[derive(Debug)] -pub struct RecipeTranslate { - pub name: &'static str, - pub instructions: Vec<&'static str>, -} -"#; - - writeln!(file, "{}", structs)?; - - Ok(()) -} - #[derive(Deserialize, Debug)] pub struct Main { ingredients: Option>, @@ -127,10 +73,7 @@ impl fmt::Debug for RecipeIngredient { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("RecipeIngredient") .field("key", &self.key) - .field( - "measure", - &format_args!("RecipeIngredientMeasure::{:?}", self.measure), - ) + .field("measure", &format_args!("Measure::{:?}", self.measure)) .finish() } } diff --git a/db/src/data.rs b/db/src/data.rs index e63a8fc..9ea17e9 100644 --- a/db/src/data.rs +++ b/db/src/data.rs @@ -1,50 +1,4 @@ -#![allow(dead_code)] - -#[derive(Debug)] -pub struct Ingredient { - pub key: &'static str, - pub translates: IngredientTranslate, -} - -#[derive(Debug)] -pub struct IngredientTranslate { - pub rus: &'static str, - pub eng: Option<&'static str>, -} - -#[derive(Debug)] -pub struct Recipe { - pub key: &'static str, - pub steps: u8, - pub ingredients: Vec, - pub translates: RecipeTranslates, -} - -#[derive(Debug)] -pub struct RecipeIngredient { - pub key: &'static str, - pub measure: RecipeIngredientMeasure, -} - -#[derive(Debug, Clone, Copy)] -pub enum RecipeIngredientMeasure { - Gram(u32), - KiloGram(u32), - MilliLiter(u32), - Liter(u32), -} - -#[derive(Debug)] -pub struct RecipeTranslates { - pub rus: RecipeTranslate, - pub eng: Option, -} - -#[derive(Debug)] -pub struct RecipeTranslate { - pub name: &'static str, - pub instructions: Vec<&'static str>, -} +use crate::types::*; pub const INGREDIENTS: [Ingredient; 11] = [ Ingredient { @@ -156,27 +110,27 @@ pub static ref RECIPES: [Recipe; 1] = [ ingredients: vec![ RecipeIngredient { key: "salt", - measure: RecipeIngredientMeasure::Gram(5), + measure: Measure::Gram(5), }, RecipeIngredient { key: "sugar", - measure: RecipeIngredientMeasure::Gram(4), + measure: Measure::Gram(4), }, RecipeIngredient { key: "wheat_flour", - measure: RecipeIngredientMeasure::Gram(500), + measure: Measure::Gram(500), }, RecipeIngredient { key: "dry_yeast", - measure: RecipeIngredientMeasure::Gram(7), + measure: Measure::Gram(7), }, RecipeIngredient { key: "olive_oil", - measure: RecipeIngredientMeasure::MilliLiter(25), + measure: Measure::MilliLiter(25), }, RecipeIngredient { key: "water", - measure: RecipeIngredientMeasure::MilliLiter(250), + measure: Measure::MilliLiter(250), }, ], translates: RecipeTranslates { diff --git a/db/src/lib.rs b/db/src/lib.rs index 0a8b6dd..df78ca1 100644 --- a/db/src/lib.rs +++ b/db/src/lib.rs @@ -1,3 +1,4 @@ pub mod data; +pub mod types; -pub use data::{INGREDIENTS, RECIPES}; +pub use types::*; diff --git a/db/src/types.rs b/db/src/types.rs new file mode 100644 index 0000000..9bcc9a3 --- /dev/null +++ b/db/src/types.rs @@ -0,0 +1,45 @@ +#[derive(Debug)] +pub struct Ingredient { + pub key: &'static str, + pub translates: IngredientTranslate, +} + +#[derive(Debug)] +pub struct IngredientTranslate { + pub rus: &'static str, + pub eng: Option<&'static str>, +} + +#[derive(Debug)] +pub struct Recipe { + pub key: &'static str, + pub steps: u8, + pub ingredients: Vec, + pub translates: RecipeTranslates, +} + +#[derive(Debug)] +pub struct RecipeIngredient { + pub key: &'static str, + pub measure: Measure, +} + +#[derive(Debug, Clone, Copy)] +pub enum Measure { + Gram(u32), + KiloGram(u32), + MilliLiter(u32), + Liter(u32), +} + +#[derive(Debug)] +pub struct RecipeTranslates { + pub rus: RecipeTranslate, + pub eng: Option, +} + +#[derive(Debug)] +pub struct RecipeTranslate { + pub name: &'static str, + pub instructions: Vec<&'static str>, +}