162 lines
4.9 KiB
Rust
162 lines
4.9 KiB
Rust
use crate::domain::{Context, Ingredient, Lang, Measure, Recipe, RecipeIngredient};
|
||
|
||
#[cfg(test)]
|
||
use crate::repo::ingredient::InMemoryIngredientRepo;
|
||
use crate::repo::ingredient::{IngredientRepo, StaticIngredientRepo};
|
||
|
||
pub trait RecipeRepo {
|
||
fn get_recipes(&self, ctx: &Context) -> Vec<Recipe>;
|
||
}
|
||
|
||
pub struct StaticRecipeRepo;
|
||
|
||
impl RecipeRepo for StaticRecipeRepo {
|
||
fn get_recipes(&self, ctx: &Context) -> Vec<Recipe> {
|
||
let ings_repo = StaticIngredientRepo;
|
||
let ings = ings_repo.get_ingredients(ctx, Default::default());
|
||
|
||
db::data::RECIPES
|
||
.iter()
|
||
.filter_map(|rec| Recipe::try_from((rec, ctx.lang, ings.clone())).ok())
|
||
.collect()
|
||
}
|
||
}
|
||
|
||
#[cfg(test)]
|
||
pub struct InMemoryRecipeRepo {
|
||
pub recipes: Vec<db::Recipe>,
|
||
}
|
||
|
||
#[cfg(test)]
|
||
impl RecipeRepo for InMemoryRecipeRepo {
|
||
fn get_recipes(&self, ctx: &Context) -> Vec<Recipe> {
|
||
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 {
|
||
key: "fruit_salad",
|
||
steps: 0,
|
||
ingredients: vec![
|
||
db::RecipeIngredient {
|
||
key: "banana",
|
||
measure: db::Measure::Gram(150),
|
||
},
|
||
db::RecipeIngredient {
|
||
key: "apple",
|
||
measure: db::Measure::Gram(150),
|
||
},
|
||
db::RecipeIngredient {
|
||
key: "orange",
|
||
measure: db::Measure::Gram(150),
|
||
},
|
||
],
|
||
translates: db::RecipeTranslates {
|
||
rus: db::RecipeTranslate {
|
||
name: "Фруктовый салат",
|
||
instructions: vec![
|
||
"Нарезать бананы кружочками",
|
||
"Нарезать яблоки и апельсины кубиками",
|
||
"Все ингредиенты перемешать",
|
||
],
|
||
},
|
||
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: db::Measure::Gram(5),
|
||
},
|
||
db::RecipeIngredient {
|
||
key: "sugar",
|
||
measure: db::Measure::Gram(4),
|
||
},
|
||
db::RecipeIngredient {
|
||
key: "wheat_flour",
|
||
measure: db::Measure::Gram(500),
|
||
},
|
||
],
|
||
translates: db::RecipeTranslates {
|
||
rus: db::RecipeTranslate {
|
||
name: "Не найдены ингредиенты",
|
||
instructions: vec![],
|
||
},
|
||
eng: None,
|
||
},
|
||
});
|
||
self
|
||
}
|
||
}
|
||
|
||
impl TryFrom<(&db::Recipe, Lang, Vec<Ingredient>)> for Recipe {
|
||
type Error = ();
|
||
|
||
fn try_from(
|
||
(db, lang, ingredients): (&db::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),
|
||
};
|
||
|
||
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
|
||
} else {
|
||
lang
|
||
},
|
||
name: ctr.name.to_string(),
|
||
instructions,
|
||
ingredients,
|
||
})
|
||
}
|
||
}
|
||
|
||
impl From<db::Measure> 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),
|
||
}
|
||
}
|
||
}
|