cosmetic changes

This commit is contained in:
Dmitriy Pleshevskiy 2022-05-15 23:25:11 +03:00
parent b9fdcb96d7
commit d15faa1f65
11 changed files with 212 additions and 299 deletions

View File

@ -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<Vec<String>>,
}
pub fn execute(
repo: &impl IngredientRepo,
ctx: &Context,
opts: RequestOpts,
) -> Vec<types::Ingredient> {
pub fn execute(repo: &impl IngredientRepo, ctx: &Context, opts: RequestOpts) -> Vec<Ingredient> {
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() {

View File

@ -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(),
}
}
}

View File

@ -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};

View File

@ -10,7 +10,7 @@ pub fn execute(repo: &impl RecipeRepo, ctx: &Context) -> Vec<types::Recipe> {
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!(),
}

View File

@ -9,60 +9,17 @@ pub struct Recipe {
pub ingredients: Vec<RecipeIngredient>,
}
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),
};
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,
})
}
}
#[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<db::data::RecipeIngredientMeasure> 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),
}
}
}

View File

@ -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<misc_types::Lang>,
}
impl From<fetch_by_key::RequestOpts> 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<fetch_list::RequestOpts> for GetIngredientsOpts {
}
pub trait IngredientRepo {
fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option<types::Ingredient>;
fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option<Ingredient>;
fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec<types::Ingredient>;
fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec<Ingredient>;
}
pub struct StaticIngredientRepo;
impl IngredientRepo for StaticIngredientRepo {
fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option<types::Ingredient> {
db::INGREDIENTS
fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option<Ingredient> {
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<types::Ingredient> {
let langs = [ctx.lang].repeat(db::INGREDIENTS.len());
db::INGREDIENTS
fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec<Ingredient> {
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::<Vec<_>>()
}
@ -52,7 +41,27 @@ impl IngredientRepo for StaticIngredientRepo {
#[cfg(test)]
pub struct InMemoryIngredientRepo {
pub ingredients: Vec<db::data::Ingredient>,
pub ingredients: Vec<db::Ingredient>,
}
#[cfg(test)]
impl IngredientRepo for InMemoryIngredientRepo {
fn get_ingredient_opt(&self, ctx: &Context, key: String) -> Option<Ingredient> {
self.ingredients
.iter()
.find(|ing| ing.key == &key)
.map(|ing| Ingredient::from((ing, ctx.lang)))
}
fn get_ingredients(&self, ctx: &Context, opts: GetIngredientsOpts) -> Vec<Ingredient> {
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::<Vec<_>>()
}
}
#[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<types::Ingredient> {
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<types::Ingredient> {
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::<Vec<_>>()
Self {
key: db.key.to_string(),
lang: if name == tr.rus { Lang::Rus } else { lang },
name: name.to_string(),
}
}
}

View File

@ -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<types::Recipe>;
fn get_recipes(&self, ctx: &Context) -> Vec<Recipe>;
}
pub struct StaticRecipeRepo;
impl RecipeRepo for StaticRecipeRepo {
fn get_recipes(&self, ctx: &Context) -> Vec<types::Recipe> {
let ings_repo = repo::ingredient::StaticIngredientRepo;
fn get_recipes(&self, ctx: &Context) -> Vec<Recipe> {
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<db::data::Recipe>,
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 {
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<types::Recipe> {
let ings_repo = repo::ingredient::InMemoryIngredientRepo::new();
let ings = ings_repo.get_ingredients(ctx, Default::default());
impl TryFrom<(&db::Recipe, Lang, Vec<Ingredient>)> for Recipe {
type Error = ();
let langs = [ctx.lang].repeat(self.recipes.len());
self.recipes
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()
.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::<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),
}
}
}

View File

@ -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<fs::File>) -> 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<RecipeIngredient>,
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<RecipeTranslate>,
}
#[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<Vec<Ingredient>>,
@ -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()
}
}

View File

@ -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<RecipeIngredient>,
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<RecipeTranslate>,
}
#[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 {

View File

@ -1,3 +1,4 @@
pub mod data;
pub mod types;
pub use data::{INGREDIENTS, RECIPES};
pub use types::*;

45
db/src/types.rs Normal file
View File

@ -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<RecipeIngredient>,
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<RecipeTranslate>,
}
#[derive(Debug)]
pub struct RecipeTranslate {
pub name: &'static str,
pub instructions: Vec<&'static str>,
}