refac(rest): move server and resolvers to rest layer
This commit is contained in:
parent
4626157319
commit
e976651169
|
@ -5,8 +5,8 @@ extern crate serde;
|
||||||
|
|
||||||
mod domain;
|
mod domain;
|
||||||
mod repo;
|
mod repo;
|
||||||
mod server;
|
mod rest;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
server::start();
|
rest::server::start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
use std::io::Cursor;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use tiny_http::{Header, Response};
|
||||||
|
|
||||||
|
use crate::domain;
|
||||||
|
use crate::domain::ingredient::types::Lang;
|
||||||
|
use crate::repo::ingredient::StaticIngredientRepo;
|
||||||
|
use crate::rest::types::{QueryParams, Url};
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
struct FetchIngredientsOpts<'a> {
|
||||||
|
lang: Option<&'a str>,
|
||||||
|
keys: Option<Vec<&'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),
|
||||||
|
("key", val) => {
|
||||||
|
let mut keys = opts.keys.unwrap_or_default();
|
||||||
|
keys.push(val);
|
||||||
|
opts.keys = Some(keys)
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
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());
|
||||||
|
let keys = rest
|
||||||
|
.keys
|
||||||
|
.map(|keys| keys.into_iter().map(String::from).collect());
|
||||||
|
Self { lang, keys }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fetch_list(url: &Url) -> Response<Cursor<Vec<u8>>> {
|
||||||
|
use domain::ingredient::fetch_list;
|
||||||
|
let opts = FetchIngredientsOpts::from(url.query_params());
|
||||||
|
|
||||||
|
let repo = StaticIngredientRepo;
|
||||||
|
let ingredients = fetch_list::execute(&repo, opts.into());
|
||||||
|
let data = serde_json::to_string(&ingredients).unwrap();
|
||||||
|
|
||||||
|
Response::from_string(data)
|
||||||
|
.with_header(Header::from_str("content-type: application/json").unwrap())
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod ingredient;
|
|
@ -0,0 +1,3 @@
|
||||||
|
pub mod ctrl;
|
||||||
|
pub mod server;
|
||||||
|
pub mod types;
|
|
@ -0,0 +1,34 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::thread;
|
||||||
|
use tiny_http::{Response, Server};
|
||||||
|
|
||||||
|
use crate::rest;
|
||||||
|
use crate::rest::types::Url;
|
||||||
|
|
||||||
|
pub fn start() {
|
||||||
|
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);
|
||||||
|
|
||||||
|
for _ in 0..4 {
|
||||||
|
let server = server.clone();
|
||||||
|
|
||||||
|
handles.push(thread::spawn(move || {
|
||||||
|
for rq in server.incoming_requests() {
|
||||||
|
let url = Url::parse(rq.url());
|
||||||
|
let _ = match url.path_segments()[..] {
|
||||||
|
["api", "ingredients"] => {
|
||||||
|
let res = rest::ctrl::ingredient::fetch_list(&url);
|
||||||
|
rq.respond(res)
|
||||||
|
}
|
||||||
|
_ => rq.respond(Response::from_string("Not found")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for h in handles {
|
||||||
|
h.join().unwrap();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Url<'a> {
|
||||||
|
path: &'a str,
|
||||||
|
query: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type QueryParam<'a> = (&'a str, &'a str);
|
||||||
|
|
||||||
|
pub 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().trim_matches('/');
|
||||||
|
let query = parts.next();
|
||||||
|
|
||||||
|
Url { path, query }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path_segments(&self) -> Vec<&str> {
|
||||||
|
self.path.split('/').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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,122 +0,0 @@
|
||||||
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() {
|
|
||||||
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);
|
|
||||||
|
|
||||||
for _ in 0..4 {
|
|
||||||
let server = server.clone();
|
|
||||||
|
|
||||||
handles.push(thread::spawn(move || {
|
|
||||||
for rq in server.incoming_requests() {
|
|
||||||
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 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")),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for h in handles {
|
|
||||||
h.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
|
||||||
struct FetchIngredientsOpts<'a> {
|
|
||||||
lang: Option<&'a str>,
|
|
||||||
keys: Option<Vec<&'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),
|
|
||||||
("key", val) => {
|
|
||||||
let mut keys = opts.keys.unwrap_or_default();
|
|
||||||
keys.push(val);
|
|
||||||
opts.keys = Some(keys)
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
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());
|
|
||||||
let keys = rest
|
|
||||||
.keys
|
|
||||||
.map(|keys| keys.into_iter().map(String::from).collect());
|
|
||||||
Self { lang, keys }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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