api: filter recipes if ingredient not exist in db
This commit is contained in:
parent
01216f2bbe
commit
c19407940b
|
@ -20,7 +20,7 @@ mod tests {
|
|||
let res = execute(&repo);
|
||||
|
||||
match res.as_slice() {
|
||||
[salad, pizza_base] => {
|
||||
[salad] => {
|
||||
assert_eq!(salad.key, String::from("fruit_salad"));
|
||||
assert_eq!(salad.lang, Lang::Rus);
|
||||
assert_eq!(salad.name, String::from("Фруктовый салат"));
|
||||
|
@ -44,13 +44,32 @@ mod tests {
|
|||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(pizza_base.key, String::from("thin_crispy_pizza_base"));
|
||||
assert_eq!(pizza_base.lang, Lang::Rus);
|
||||
assert_eq!(
|
||||
pizza_base.name,
|
||||
String::from("Тонкая хрустящая основа для пиццы")
|
||||
);
|
||||
#[test]
|
||||
fn should_not_return_recipes_if_ingredients_not_exist_in_db() {
|
||||
let mut repo = InMemoryRecipeRepo::new().with_no_ingredients_found();
|
||||
let res = execute(&repo);
|
||||
|
||||
match res.as_slice() {
|
||||
[salad] => {
|
||||
assert_eq!(salad.key, String::from("fruit_salad"));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
if let Some(rec) = repo.recipes.get_mut(1) {
|
||||
rec.ingredients.pop(); // remove wheat flour from ingredients
|
||||
}
|
||||
|
||||
let res = execute(&repo);
|
||||
match res.as_slice() {
|
||||
[salad, no_found] => {
|
||||
assert_eq!(salad.key, String::from("fruit_salad"));
|
||||
assert_eq!(no_found.key, String::from("no_ingredients_found"));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
|
|
@ -9,15 +9,36 @@ pub struct Recipe {
|
|||
pub ingredients: Vec<RecipeIngredient>,
|
||||
}
|
||||
|
||||
impl From<(&db::data::Recipe, Lang, Vec<Ingredient>)> for Recipe {
|
||||
fn from((db, lang, ingredients): (&db::data::Recipe, Lang, Vec<Ingredient>)) -> Self {
|
||||
impl TryFrom<(&db::data::Recipe, Lang, Vec<Ingredient>)> for Recipe {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(
|
||||
(db, lang, ingredients): (&db::data::Recipe, Lang, Vec<Ingredient>),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let tr = &db.translates;
|
||||
let ctr = match lang {
|
||||
Lang::Rus => &tr.rus,
|
||||
Lang::Eng => tr.eng.as_ref().unwrap_or(&tr.rus),
|
||||
};
|
||||
|
||||
Self {
|
||||
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::<Result<Vec<_>, _>>()?;
|
||||
|
||||
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
|
||||
|
@ -25,21 +46,9 @@ impl From<(&db::data::Recipe, Lang, Vec<Ingredient>)> for Recipe {
|
|||
lang
|
||||
},
|
||||
name: ctr.name.to_string(),
|
||||
instructions: ctr.instructions.iter().copied().map(String::from).collect(),
|
||||
ingredients: db
|
||||
.ingredients
|
||||
.iter()
|
||||
.filter_map(|sing| {
|
||||
ingredients
|
||||
.iter()
|
||||
.find(|ing| sing.key == ing.key)
|
||||
.map(|ing| RecipeIngredient {
|
||||
ingredient: ing.clone(),
|
||||
measure: sing.measure.into(),
|
||||
})
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
instructions,
|
||||
ingredients,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use crate::domain::{misc_types::Lang, recipe::types};
|
||||
use crate::repo;
|
||||
|
||||
use super::ingredient::IngredientRepo;
|
||||
|
||||
use db::data as Db;
|
||||
use Db::RecipeIngredientMeasure as DbRIM;
|
||||
|
||||
pub trait RecipeRepo {
|
||||
fn get_recipes(&self) -> Vec<types::Recipe>;
|
||||
}
|
||||
|
@ -10,121 +14,100 @@ pub struct StaticRecipeRepo;
|
|||
|
||||
impl RecipeRepo for StaticRecipeRepo {
|
||||
fn get_recipes(&self) -> Vec<types::Recipe> {
|
||||
let ings_repo = crate::repo::ingredient::StaticIngredientRepo;
|
||||
let ings_repo = repo::ingredient::StaticIngredientRepo;
|
||||
let ings = ings_repo.get_ingredients(Default::default());
|
||||
|
||||
let langs = [Lang::default()].repeat(db::RECIPES.len());
|
||||
db::RECIPES
|
||||
.iter()
|
||||
.zip(langs)
|
||||
.map(|(rec, lang)| From::from((rec, lang, ings.clone())))
|
||||
.filter_map(|(rec, lang)| types::Recipe::try_from((rec, lang, ings.clone())).ok())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub struct InMemoryRecipeRepo {
|
||||
recipes: Vec<db::data::Recipe>,
|
||||
pub recipes: Vec<db::data::Recipe>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl InMemoryRecipeRepo {
|
||||
pub fn new() -> Self {
|
||||
use db::data as Db;
|
||||
use Db::RecipeIngredientMeasure as DbRIM;
|
||||
|
||||
Self {
|
||||
recipes: vec![
|
||||
Db::Recipe {
|
||||
key: "fruit_salad",
|
||||
steps: 0,
|
||||
ingredients: vec![
|
||||
Db::RecipeIngredient {
|
||||
key: "banana",
|
||||
measure: DbRIM::Gram(150),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "apple",
|
||||
measure: DbRIM::Gram(150),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "orange",
|
||||
measure: DbRIM::Gram(150),
|
||||
},
|
||||
],
|
||||
translates: Db::RecipeTranslates {
|
||||
rus: Db::RecipeTranslate {
|
||||
name: "Фруктовый салат",
|
||||
instructions: vec![
|
||||
"Нарезать бананы кружочками",
|
||||
"Нарезать яблоки и апельсины кубиками",
|
||||
"Все ингредиенты перемешать",
|
||||
],
|
||||
},
|
||||
eng: None,
|
||||
recipes: vec![Db::Recipe {
|
||||
key: "fruit_salad",
|
||||
steps: 0,
|
||||
ingredients: vec![
|
||||
Db::RecipeIngredient {
|
||||
key: "banana",
|
||||
measure: DbRIM::Gram(150),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "apple",
|
||||
measure: DbRIM::Gram(150),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "orange",
|
||||
measure: DbRIM::Gram(150),
|
||||
},
|
||||
],
|
||||
translates: Db::RecipeTranslates {
|
||||
rus: Db::RecipeTranslate {
|
||||
name: "Фруктовый салат",
|
||||
instructions: vec![
|
||||
"Нарезать бананы кружочками",
|
||||
"Нарезать яблоки и апельсины кубиками",
|
||||
"Все ингредиенты перемешать",
|
||||
],
|
||||
},
|
||||
eng: None,
|
||||
},
|
||||
Db::Recipe {
|
||||
key: "thin_crispy_pizza_base",
|
||||
steps: 7,
|
||||
ingredients: vec![
|
||||
Db::RecipeIngredient {
|
||||
key: "salt",
|
||||
measure: DbRIM::Gram(5),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "sugar",
|
||||
measure: DbRIM::Gram(4),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "wheat_flour",
|
||||
measure: DbRIM::Gram(500),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "dry_yeast",
|
||||
measure: DbRIM::Gram(7),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "olive_oil",
|
||||
measure: DbRIM::MilliLiter(25),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "water",
|
||||
measure: DbRIM::MilliLiter(250),
|
||||
},
|
||||
],
|
||||
translates: Db::RecipeTranslates {
|
||||
rus: Db::RecipeTranslate {
|
||||
name: "Тонкая хрустящая основа для пиццы",
|
||||
instructions: vec![
|
||||
"Растворить дрожжи в воде, подогретой до температуры тела.",
|
||||
"Добавить соль, сахар, оливковое масло и хорошо перемешать до однородной консистенции.",
|
||||
"Добавить муку и замесить тесто. Вымешивать не менее 15 минут.",
|
||||
"Разделить тесто на 3 порции, каждую завернуть в пищевую плёнку и дать настояться около 30 минут.",
|
||||
"Растянуть один кусок теста до тонкого состояния и аккуратно переложить на смазанный растительным маслом противень.",
|
||||
"Смазать соусом, чтобы тесто не осталось жидким и чтобы начинка не скользила по нему, и поставить в предварительно разогретую до максимальной температуры (не меньше 250 градусов) на 5-10 минут. В результате тесто хорошо пропечется и станет хрустящим.",
|
||||
"Теперь на основу можно выкладывать начинку на ваш вкус и запекать до момента, когда сыр растает.",
|
||||
],
|
||||
},
|
||||
eng: None,
|
||||
},
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_no_ingredients_found(mut self) -> Self {
|
||||
self.recipes.push(Db::Recipe {
|
||||
key: "no_ingredients_found",
|
||||
steps: 0,
|
||||
ingredients: vec![
|
||||
Db::RecipeIngredient {
|
||||
key: "salt",
|
||||
measure: DbRIM::Gram(5),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "sugar",
|
||||
measure: DbRIM::Gram(4),
|
||||
},
|
||||
Db::RecipeIngredient {
|
||||
key: "wheat_flour",
|
||||
measure: DbRIM::Gram(500),
|
||||
},
|
||||
],
|
||||
}
|
||||
translates: Db::RecipeTranslates {
|
||||
rus: Db::RecipeTranslate {
|
||||
name: "Не найдены ингредиенты",
|
||||
instructions: vec![],
|
||||
},
|
||||
eng: None,
|
||||
},
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl RecipeRepo for InMemoryRecipeRepo {
|
||||
fn get_recipes(&self) -> Vec<types::Recipe> {
|
||||
let ings_repo = crate::repo::ingredient::InMemoryIngredientRepo::new();
|
||||
let ings_repo = repo::ingredient::InMemoryIngredientRepo::new();
|
||||
let ings = ings_repo.get_ingredients(Default::default());
|
||||
|
||||
let langs = [Lang::default()].repeat(self.recipes.len());
|
||||
self.recipes
|
||||
.iter()
|
||||
.zip(langs)
|
||||
.map(|(rec, lang)| From::from((rec, lang, ings.clone())))
|
||||
.filter_map(|(rec, lang)| types::Recipe::try_from((rec, lang, ings.clone())).ok())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue