api: filter ingredients by lang
This commit is contained in:
parent
661b178612
commit
8c367615a1
|
@ -1,9 +1,9 @@
|
|||
use super::types;
|
||||
use crate::repo::ingredient::{GetIngredientOpts, IngredientRepo};
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Default, Debug)]
|
||||
pub struct RequestOpts {
|
||||
lang: Option<types::Lang>,
|
||||
pub lang: Option<types::Lang>,
|
||||
}
|
||||
|
||||
pub fn execute(repo: &impl IngredientRepo, opts: RequestOpts) -> Vec<types::Ingredient> {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize)]
|
||||
pub enum Lang {
|
||||
Rus,
|
||||
|
@ -11,6 +13,18 @@ impl Default for Lang {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Lang {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"Rus" | "rus" => Ok(Lang::Rus),
|
||||
"Eng" | "eng" => Ok(Lang::Eng),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Ingredient {
|
||||
pub key: String,
|
||||
|
|
|
@ -9,7 +9,7 @@ pub trait IngredientRepo {
|
|||
fn get_ingredients(&self, opts: GetIngredientOpts) -> Vec<types::Ingredient>;
|
||||
}
|
||||
|
||||
pub struct StaticIngredientRepo {}
|
||||
pub struct StaticIngredientRepo;
|
||||
|
||||
impl IngredientRepo for StaticIngredientRepo {
|
||||
fn get_ingredients(&self, opts: GetIngredientOpts) -> Vec<types::Ingredient> {
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use crate::domain;
|
||||
use crate::domain::ingredient::types::Lang;
|
||||
use crate::repo::ingredient::StaticIngredientRepo;
|
||||
|
||||
pub fn start() {
|
||||
let server = Arc::new(tiny_http::Server::http("0.0.0.0:33333").unwrap());
|
||||
println!("Server listening on port 33333");
|
||||
use tiny_http::{Header, Response, Server};
|
||||
let server = Arc::new(Server::http("0.0.0.0:33333").unwrap());
|
||||
println!("Server listening on http://localhost:33333");
|
||||
|
||||
let mut handles = Vec::with_capacity(4);
|
||||
|
||||
|
@ -15,13 +18,23 @@ pub fn start() {
|
|||
|
||||
handles.push(thread::spawn(move || {
|
||||
for rq in server.incoming_requests() {
|
||||
use domain::ingredient::fetch_list;
|
||||
let repo = StaticIngredientRepo {};
|
||||
let ingredients = fetch_list::execute(&repo, fetch_list::RequestOpts::default());
|
||||
let data = serde_json::to_string(&ingredients).unwrap();
|
||||
let url = Url::parse(rq.url());
|
||||
let _ = match url.path_segments()[..] {
|
||||
["ingredients"] => {
|
||||
use domain::ingredient::fetch_list;
|
||||
let opts = FetchIngredientsOpts::from(url.query_params());
|
||||
|
||||
let response = tiny_http::Response::from_string(data);
|
||||
let _ = rq.respond(response);
|
||||
let repo = StaticIngredientRepo;
|
||||
let ingredients = fetch_list::execute(&repo, opts.into());
|
||||
let data = serde_json::to_string(&ingredients).unwrap();
|
||||
|
||||
let response = Response::from_string(data).with_header(
|
||||
Header::from_str("content-type: application/json").unwrap(),
|
||||
);
|
||||
rq.respond(response)
|
||||
}
|
||||
_ => rq.respond(Response::from_string("Not found")),
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
@ -30,3 +43,71 @@ pub fn start() {
|
|||
h.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct FetchIngredientsOpts<'a> {
|
||||
lang: Option<&'a str>,
|
||||
}
|
||||
|
||||
impl<'a> From<QueryParams<'a>> for FetchIngredientsOpts<'a> {
|
||||
fn from(params: QueryParams<'a>) -> Self {
|
||||
params
|
||||
.into_iter()
|
||||
.fold(FetchIngredientsOpts::default(), |mut opts, p| {
|
||||
match p {
|
||||
("lang", val) => opts.lang = Some(val),
|
||||
_ => {}
|
||||
};
|
||||
|
||||
opts
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FetchIngredientsOpts<'_>> for domain::ingredient::fetch_list::RequestOpts {
|
||||
fn from(rest: FetchIngredientsOpts) -> Self {
|
||||
let lang = rest.lang.and_then(|l| Lang::from_str(l).ok());
|
||||
Self { lang }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Url<'a> {
|
||||
path: &'a str,
|
||||
query: Option<&'a str>,
|
||||
}
|
||||
|
||||
type QueryParam<'a> = (&'a str, &'a str);
|
||||
|
||||
type QueryParams<'a> = Vec<QueryParam<'a>>;
|
||||
|
||||
impl Url<'_> {
|
||||
pub fn parse(url: &str) -> Url {
|
||||
let mut parts = url.splitn(2, '?');
|
||||
|
||||
let path = parts.next().unwrap_or_default();
|
||||
let query = parts.next();
|
||||
|
||||
Url { path, query }
|
||||
}
|
||||
|
||||
pub fn path_segments(&self) -> Vec<&str> {
|
||||
self.path.split('/').skip(1).collect()
|
||||
}
|
||||
|
||||
pub fn query_params(&self) -> Vec<QueryParam> {
|
||||
self.query
|
||||
.map(|q| {
|
||||
q.split('&')
|
||||
.filter_map(|part| {
|
||||
if let [key, val] = part.splitn(2, '=').collect::<Vec<&str>>()[..] {
|
||||
Some((key, val))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue