core: add recipes domain types

This commit is contained in:
Dmitriy Pleshevskiy 2023-03-16 19:05:45 +03:00
parent 8a62dfee70
commit 1a7c1f099c
Signed by: pleshevskiy
GPG Key ID: 79C4487B44403985
16 changed files with 67 additions and 189 deletions

View File

@ -2,6 +2,7 @@
name = "back_core"
version = "0.1.0"
edition = "2021"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,3 +1,5 @@
mod components;
mod port;
mod shared;
#[macro_use]
pub mod shared;
pub mod components;
pub mod port;

View File

@ -1 +1,2 @@
pub mod ingredient;
pub mod recipe;

View File

@ -1,8 +1,10 @@
use crate::app_core::shared::domain::Lang;
use crate::app_core::shared::{domain::Lang, lib::EntityId};
#[derive(Debug, Clone)]
pub struct Ingredient {
pub key: String,
pub key: EntityId,
pub lang: Lang,
pub name: String,
}
impl_base_entity!(Ingredient);

View File

@ -0,0 +1,3 @@
mod domain;
pub use domain::*;

View File

@ -0,0 +1,7 @@
mod measure;
mod recipe;
mod recipe_ingredient;
pub use measure::Measure;
pub use recipe::Recipe;
pub use recipe_ingredient::RecipeIngredient;

View File

@ -0,0 +1,7 @@
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Measure {
Gram(u32),
KiloGram(u32),
MilliLiter(u32),
Liter(u32),
}

View File

@ -0,0 +1,14 @@
use crate::app_core::shared::{domain::Lang, lib::EntityId};
use super::RecipeIngredient;
#[derive(Debug, Clone)]
pub struct Recipe {
pub key: EntityId,
pub lang: Lang,
pub name: String,
pub instructions: Vec<String>,
pub ingredients: Vec<RecipeIngredient>,
}
impl_base_entity!(Recipe);

View File

@ -0,0 +1,9 @@
use crate::app_core::components::ingredient::Ingredient;
use super::Measure;
#[derive(Debug, Clone)]
pub struct RecipeIngredient {
pub ingredient: Ingredient,
pub measure: Measure,
}

View File

@ -3,7 +3,7 @@ mod recipe;
use crate::app_core::shared::lib::{Entity, EntityId};
use shaku::{HasComponent, HasProvider, Interface};
use shaku::Interface;
#[derive(Debug)]
pub enum GetDbConnectionError {
@ -45,11 +45,3 @@ pub trait RepositoryPort: Interface {
async fn find_one_opt(&self, id: &EntityId) -> Result<Option<Self::Entity>, FindOneOptError>;
}
pub trait InfraDatabaseModulePort<M: ManageConnection>:
HasComponent<dyn DbConnectionPoolPort<M>>
+ HasProvider<dyn UserRepositoryPort>
+ HasProvider<dyn CompanyRepositoryPort>
+ HasProvider<dyn ContactRepositoryPort>
{
}

View File

@ -1,9 +1,4 @@
use super::RepositoryPort;
use crate::app_core::components::ingredient::Ingredient;
#[derive(Default)]
pub struct GetIngredientsOpts {
pub keys: Option<Vec<String>>,
}
pub trait IngredientRepoPort: RepositoryPort<Entity = Ingredient> {}

View File

@ -1,161 +1,5 @@
use crate::domain::{Context, Ingredient, Lang, Measure, Recipe, RecipeIngredient};
use crate::app_core::components::recipe::Recipe;
#[cfg(test)]
use crate::repo::ingredient::InMemoryIngredientRepo;
use crate::repo::ingredient::{IngredientRepo, StaticIngredientRepo};
use super::RepositoryPort;
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),
}
}
}
pub trait RecipeRepoPort: RepositoryPort<Entity = Recipe> {}

View File

@ -1,2 +1,3 @@
pub mod domain;
#[macro_use]
pub mod lib;

View File

@ -1,4 +1,6 @@
#[macro_use]
mod entity;
mod use_case;
mod value_object;

View File

@ -1,9 +1,9 @@
use core::fmt;
pub type EntityId = uuid::Uuid;
pub type EntityId = String;
pub trait Entity: fmt::Debug + PartialEq + Clone + Send + Sync {
fn id(&self) -> &EntityId;
fn key(&self) -> &EntityId;
}
macro_rules! impl_entity_partial_eq {
@ -11,8 +11,8 @@ macro_rules! impl_entity_partial_eq {
impl PartialEq for $entity_name {
#[inline]
fn eq(&self, other: &Self) -> bool {
use crate::app_core::ddd::Entity as _;
self.id() == other.id()
use crate::app_core::shared::lib::Entity as _;
self.key() == other.key()
}
}
};
@ -22,10 +22,10 @@ macro_rules! impl_base_entity {
($entity_name:ident) => {
impl_entity_partial_eq!($entity_name);
impl crate::app_core::ddd::Entity for $entity_name {
impl crate::app_core::shared::lib::Entity for $entity_name {
#[inline]
fn id(&self) -> &crate::app_core::ddd::EntityId {
&self.id
fn key(&self) -> &crate::app_core::shared::lib::EntityId {
&self.key
}
}
};

View File

@ -1,18 +1,16 @@
#![allow(where_clauses_object_safety)]
#![deny(clippy::all)]
#[macro_use]
extern crate derive_builder;
#[macro_use]
extern crate async_trait;
#[macro_use]
extern crate shaku;
#[cfg(test)]
#[macro_use]
extern crate mockall;
// App core
#[macro_use]
pub mod app_core;
// Infrastructure
pub mod infra;
// pub mod infra;