api: filter ingredients by lang
This commit is contained in:
parent
830ea2b9de
commit
661b178612
|
@ -1,8 +1,13 @@
|
|||
use super::types;
|
||||
use crate::repo::ingredient::IngredientRepo;
|
||||
use crate::repo::ingredient::{GetIngredientOpts, IngredientRepo};
|
||||
|
||||
pub fn execute(repo: &impl IngredientRepo) -> Vec<types::Ingredient> {
|
||||
repo.get_ingredients()
|
||||
#[derive(Default)]
|
||||
pub struct RequestOpts {
|
||||
lang: Option<types::Lang>,
|
||||
}
|
||||
|
||||
pub fn execute(repo: &impl IngredientRepo, opts: RequestOpts) -> Vec<types::Ingredient> {
|
||||
repo.get_ingredients(GetIngredientOpts { lang: opts.lang })
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -13,16 +18,50 @@ mod tests {
|
|||
#[test]
|
||||
fn should_return_all_ingredients() {
|
||||
let repo = crate::repo::ingredient::InMemoryIngredientRepo::new();
|
||||
let res = execute(&repo);
|
||||
let res = execute(&repo, RequestOpts::default());
|
||||
|
||||
match res.as_slice() {
|
||||
[first, second] => {
|
||||
assert_eq!(first.key, String::from("apple"));
|
||||
assert_eq!(first.lang, Lang::Rus);
|
||||
assert_eq!(first.name, String::from("Яблоко"));
|
||||
assert_eq!(second.key, String::from("salt"));
|
||||
assert_eq!(second.lang, Lang::Rus);
|
||||
assert_eq!(second.name, String::from("Соль"));
|
||||
[apple, orange, salt, sugar] => {
|
||||
assert_eq!(apple.key, String::from("apple"));
|
||||
assert_eq!(apple.lang, Lang::Rus);
|
||||
assert_eq!(apple.name, String::from("Яблоко"));
|
||||
|
||||
assert_eq!(orange.key, String::from("orange"));
|
||||
assert_eq!(orange.lang, Lang::Rus);
|
||||
assert_eq!(orange.name, String::from("Апельсин"));
|
||||
|
||||
assert_eq!(salt.key, String::from("salt"));
|
||||
assert_eq!(salt.lang, Lang::Rus);
|
||||
assert_eq!(salt.name, String::from("Соль"));
|
||||
|
||||
assert_eq!(sugar.key, String::from("sugar"));
|
||||
assert_eq!(sugar.lang, Lang::Rus);
|
||||
assert_eq!(sugar.name, String::from("Сахар"));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_return_filtered_by_lang() {
|
||||
let repo = crate::repo::ingredient::InMemoryIngredientRepo::new();
|
||||
let res = execute(
|
||||
&repo,
|
||||
RequestOpts {
|
||||
lang: Some(Lang::Eng),
|
||||
..RequestOpts::default()
|
||||
},
|
||||
);
|
||||
|
||||
match res.as_slice() {
|
||||
[apple, salt] => {
|
||||
assert_eq!(apple.key, String::from("apple"));
|
||||
assert_eq!(apple.lang, Lang::Eng);
|
||||
assert_eq!(apple.name, String::from("Apple"));
|
||||
|
||||
assert_eq!(salt.key, String::from("salt"));
|
||||
assert_eq!(salt.lang, Lang::Eng);
|
||||
assert_eq!(salt.name, String::from("Salt"));
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
|
|
@ -5,6 +5,12 @@ pub enum Lang {
|
|||
Eng,
|
||||
}
|
||||
|
||||
impl Default for Lang {
|
||||
fn default() -> Self {
|
||||
Lang::Rus
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Ingredient {
|
||||
pub key: String,
|
||||
|
@ -14,19 +20,30 @@ pub struct Ingredient {
|
|||
|
||||
impl From<&db::data::Ingredient> for Ingredient {
|
||||
fn from(db: &db::data::Ingredient) -> Self {
|
||||
Self::from((db, Lang::Rus))
|
||||
Self::try_from((db, Lang::Rus)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&db::data::Ingredient, Lang)> for Ingredient {
|
||||
fn from((db, lang): (&db::data::Ingredient, Lang)) -> Self {
|
||||
#[derive(Debug)]
|
||||
pub enum IngredientValidError {
|
||||
TranslationNotFound,
|
||||
}
|
||||
|
||||
impl TryFrom<(&db::data::Ingredient, Lang)> for Ingredient {
|
||||
type Error = IngredientValidError;
|
||||
|
||||
fn try_from((db, lang): (&db::data::Ingredient, Lang)) -> Result<Self, Self::Error> {
|
||||
let key = db.key.to_string();
|
||||
|
||||
let name = match lang {
|
||||
Lang::Rus => db.translates.ru.to_string(),
|
||||
Lang::Eng => db.translates.en.unwrap().to_string(),
|
||||
Lang::Rus => Some(db.translates.ru),
|
||||
Lang::Eng => db.translates.en,
|
||||
};
|
||||
|
||||
Self { key, lang, name }
|
||||
if let Some(name) = name.map(String::from) {
|
||||
Ok(Self { key, lang, name })
|
||||
} else {
|
||||
Err(IngredientValidError::TranslationNotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,24 @@
|
|||
use crate::domain::ingredient::types;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GetIngredientOpts {
|
||||
pub lang: Option<types::Lang>,
|
||||
}
|
||||
|
||||
pub trait IngredientRepo {
|
||||
fn get_ingredients(&self) -> Vec<types::Ingredient>;
|
||||
fn get_ingredients(&self, opts: GetIngredientOpts) -> Vec<types::Ingredient>;
|
||||
}
|
||||
|
||||
pub struct StaticIngredientRepo {}
|
||||
|
||||
impl IngredientRepo for StaticIngredientRepo {
|
||||
fn get_ingredients(&self) -> Vec<types::Ingredient> {
|
||||
db::INGREDIENTS.iter().map(From::from).collect::<Vec<_>>()
|
||||
fn get_ingredients(&self, opts: GetIngredientOpts) -> Vec<types::Ingredient> {
|
||||
let langs = [opts.lang.unwrap_or_default()].repeat(db::INGREDIENTS.len());
|
||||
db::INGREDIENTS
|
||||
.iter()
|
||||
.zip(langs)
|
||||
.filter_map(|tup| TryFrom::try_from(tup).ok())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +39,13 @@ impl InMemoryIngredientRepo {
|
|||
en: Some("Apple"),
|
||||
},
|
||||
},
|
||||
db::data::Ingredient {
|
||||
key: "orange",
|
||||
translates: db::data::IngredientTranslate {
|
||||
ru: "Апельсин",
|
||||
en: None,
|
||||
},
|
||||
},
|
||||
db::data::Ingredient {
|
||||
key: "salt",
|
||||
translates: db::data::IngredientTranslate {
|
||||
|
@ -36,6 +53,13 @@ impl InMemoryIngredientRepo {
|
|||
en: Some("Salt"),
|
||||
},
|
||||
},
|
||||
db::data::Ingredient {
|
||||
key: "sugar",
|
||||
translates: db::data::IngredientTranslate {
|
||||
ru: "Сахар",
|
||||
en: None,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
@ -43,12 +67,12 @@ impl InMemoryIngredientRepo {
|
|||
|
||||
#[cfg(test)]
|
||||
impl IngredientRepo for InMemoryIngredientRepo {
|
||||
fn get_ingredients(&self) -> Vec<types::Ingredient> {
|
||||
let langs = [types::Lang::Rus].repeat(self.ingredients.len());
|
||||
fn get_ingredients(&self, opts: GetIngredientOpts) -> Vec<types::Ingredient> {
|
||||
let langs = [opts.lang.unwrap_or_default()].repeat(self.ingredients.len());
|
||||
self.ingredients
|
||||
.iter()
|
||||
.zip(langs)
|
||||
.map(From::from)
|
||||
.filter_map(|tup| TryFrom::try_from(tup).ok())
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@ pub fn start() {
|
|||
|
||||
handles.push(thread::spawn(move || {
|
||||
for rq in server.incoming_requests() {
|
||||
use domain::ingredient::fetch_list;
|
||||
let repo = StaticIngredientRepo {};
|
||||
let ingredients = domain::ingredient::fetch_list::execute(&repo);
|
||||
let ingredients = fetch_list::execute(&repo, fetch_list::RequestOpts::default());
|
||||
let data = serde_json::to_string(&ingredients).unwrap();
|
||||
|
||||
let response = tiny_http::Response::from_string(data);
|
||||
|
|
Loading…
Reference in New Issue