This commit is contained in:
2021-09-07 19:29:51 +02:00
parent d1a444f67d
commit 1f905f5934
5 changed files with 590 additions and 523 deletions

File diff suppressed because it is too large Load Diff

View File

View File

@@ -1,11 +1,5 @@
use std::convert::Infallible;
use serde::Serialize;
use uuid::Uuid;
use warp::http::StatusCode;
use warp::Filter;
use packager;
use uuid::Uuid;
#[derive(Debug)]
enum TripItemStatus {
@@ -95,87 +89,51 @@ impl<'a> Trip<'a> {
// println!("{:?}", item);
// }
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
success: bool,
message: String,
}
#[derive(Debug)]
struct InvalidUuid;
impl warp::reject::Reject for InvalidUuid {}
// See https://github.com/seanmonstar/warp/blob/master/examples/rejections.rs
async fn handle_rejection(err: warp::Rejection) -> Result<impl warp::Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
message = "NOT_FOUND";
code = StatusCode::NOT_FOUND;
} else if let Some(InvalidUuid) = err.find() {
code = StatusCode::BAD_REQUEST;
message = "INVALID_UUID";
} else if let Some(e) = err.find::<warp::filters::body::BodyDeserializeError>() {
message = "BAD_REQUEST";
code = StatusCode::BAD_REQUEST;
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
message = "METHOD_NOT_ALLOWED";
code = StatusCode::METHOD_NOT_ALLOWED;
} else {
// We should have expected this... Just log and say its a 500
eprintln!("unhandled rejection: {:?}", err);
message = "UNHANDLED_REJECTION";
code = StatusCode::INTERNAL_SERVER_ERROR;
}
let json = warp::reply::json(&ErrorMessage {
success: false,
code: code.as_u16(),
message: message.into(),
});
Ok(warp::reply::with_status(json, code))
}
#[tokio::main]
async fn main() {
let accept_json = warp::header::exact("accept", "application/json");
let cors = warp::cors().allow_any_origin();
// let accept_json = warp::header::exact("accept", "application/json");
// let cors = warp::cors().allow_any_origin();
let root = warp::path::end().map(|| "Hi");
let v1 = warp::path!("v1")
.and(warp::get())
.and(warp::path::end())
.map(warp::reply);
let lists = warp::path!("v1" / "lists")
.and(warp::path::end())
.and(warp::get())
.and(accept_json)
.map(|| warp::reply::json(&packager::get_lists()))
.with(&cors);
let list = warp::path!("v1" / "lists" / String)
.and(warp::path::end())
.and(warp::get())
.and(accept_json)
.and_then(|id: String| async move {
match Uuid::parse_str(&id) {
Ok(uuid) => {
let list = &packager::get_list(uuid);
match list {
Some(l) => Ok(warp::reply::json(l)),
None => Err(warp::reject::not_found()),
}
}
Err(e) => Err(warp::reject::custom(InvalidUuid)),
}
})
.with(&cors)
.recover(handle_rejection);
// let root = warp::path::end()
// .map(|| "Hi")
// .recover(packager::router::handle_rejection);
let routes = root.or(v1).or(lists).or(list);
// let v1 = warp::path!("v1")
// .and(warp::get())
// .and(warp::path::end())
// .map(warp::reply)
// .recover(packager::router::handle_rejection);
warp::serve(routes).run(([127, 0, 0, 1], 9000)).await;
// let lists = warp::path!("v1" / "lists")
// .and(warp::path::end())
// .and(warp::get())
// .and(accept_json)
// .map(|| warp::reply::json(&packager::get_lists()))
// .with(&cors)
// .recover(packager::router::handle_rejection);
// let list = warp::path!("v1" / "lists" / String)
// .and(warp::path::end())
// .and(warp::get())
// .and(accept_json)
// .and_then(|id: String| async move {
// match Uuid::parse_str(&id) {
// Ok(uuid) => {
// let list = &packager::get_list(uuid);
// match list {
// Some(l) => Ok(warp::reply::json(l)),
// None => Err(warp::reject::not_found()),
// }
// }
// Err(e) => Err(warp::reject::custom(packager::router::InvalidUuid)),
// }
// })
// .with(&cors)
// .recover(packager::router::handle_rejection);
// let router = root.or(v1).or(lists).or(list);
let router = packager::router::new();
warp::serve(router).run(([127, 0, 0, 1], 9000)).await;
}

226
api/src/packagelist.rs Normal file
View File

@@ -0,0 +1,226 @@
use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Duration {
None,
Days(i32),
}
impl Duration {
pub fn is_none(d: &Duration) -> bool {
matches!(d, Duration::None)
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Period {
Daily(i32),
Weekly(i32),
Days(i32),
}
impl Serialize for Period {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Period::Daily(i) => {
let mut s = serializer.serialize_struct("period", 2)?;
s.serialize_field("type", "daily")?;
s.serialize_field("value", &i)?;
s.end()
}
Period::Weekly(i) => {
let mut s = serializer.serialize_struct("period", 2)?;
s.serialize_field("type", "weekly")?;
s.serialize_field("value", &i)?;
s.end()
}
Period::Days(i) => {
let mut s = serializer.serialize_struct("period", 2)?;
s.serialize_field("type", "days")?;
s.serialize_field("value", &i)?;
s.end()
}
}
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ItemUsage {
Singleton,
Periodic(Period),
Infinite,
}
impl Serialize for ItemUsage {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
ItemUsage::Singleton => {
let mut s = serializer.serialize_struct("usage", 1)?;
s.serialize_field("type", "singleton")?;
s.end()
}
ItemUsage::Periodic(p) => {
let mut s = serializer.serialize_struct("usage", 2)?;
s.serialize_field("type", "peridoic")?;
s.serialize_field("value", &p)?;
s.end()
}
ItemUsage::Infinite => {
let mut s = serializer.serialize_struct("size", 1)?;
s.serialize_field("type", "infinite")?;
s.end()
}
}
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum ItemSize {
None,
Pack(i32),
Name(String),
Gram(i32),
}
impl Serialize for ItemSize {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
ItemSize::None => {
let mut s = serializer.serialize_struct("size", 1)?;
s.serialize_field("type", "none")?;
s.end()
}
ItemSize::Pack(i) => {
let mut s = serializer.serialize_struct("size", 2)?;
s.serialize_field("type", "pack")?;
s.serialize_field("value", &i)?;
s.end()
}
ItemSize::Name(n) => {
let mut s = serializer.serialize_struct("size", 2)?;
s.serialize_field("type", "name")?;
s.serialize_field("value", &n)?;
s.end()
}
ItemSize::Gram(i) => {
let mut s = serializer.serialize_struct("size", 2)?;
s.serialize_field("type", "gram")?;
s.serialize_field("value", &i)?;
s.end()
}
}
}
}
impl ItemSize {
pub fn is_none(d: &ItemSize) -> bool {
matches!(d, ItemSize::None)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PreparationStep {
name: String,
#[serde(skip_serializing_if = "Duration::is_none")]
start: Duration,
}
impl PreparationStep {
pub fn new(name: String, start: Duration) -> PreparationStep {
PreparationStep { name, start }
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Preparation {
None,
Steps(Vec<PreparationStep>),
}
impl Preparation {
pub fn is_none(d: &Preparation) -> bool {
matches!(d, Preparation::None)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageItem {
pub id: Uuid,
name: String,
#[serde(skip_serializing_if = "ItemSize::is_none")]
size: ItemSize,
count: i32,
usage: ItemUsage,
#[serde(skip_serializing_if = "Preparation::is_none")]
preparation: Preparation,
}
impl PackageItem {
pub fn new(
id: Uuid,
name: String,
size: ItemSize,
count: i32,
usage: ItemUsage,
preparation: Preparation,
) -> PackageItem {
PackageItem {
id,
name,
size,
count,
usage,
preparation,
}
}
pub fn new_simple(id: Uuid, name: String) -> PackageItem {
PackageItem::new(
id,
name,
ItemSize::None,
1,
ItemUsage::Singleton,
Preparation::None,
)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PackageList {
pub id: Uuid,
pub name: String,
pub items: Vec<PackageItem>,
}
impl PackageList {
pub fn new_from_items(id: Uuid, name: String, items: Vec<PackageItem>) -> PackageList {
PackageList { id, name, items }
}
}

96
api/src/router.rs Normal file
View File

@@ -0,0 +1,96 @@
use std::convert::Infallible;
use serde::Serialize;
use warp;
use warp::http::StatusCode;
use uuid::Uuid;
#[derive(Debug)]
struct InvalidUuid;
impl warp::reject::Reject for InvalidUuid {}
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
success: bool,
message: String,
}
pub fn new() -> warp::filters::BoxedFilter<(impl warp::Reply,)> {
let accept_json = warp::header::exact("accept", "application/json");
let cors = warp::cors().allow_any_origin();
let root = warp::path::end()
.map(|| "Hi")
.recover(handle_rejection);
let v1 = warp::path!("v1")
.and(warp::get())
.and(warp::path::end())
.map(warp::reply)
.recover(handle_rejection);
let lists = warp::path!("v1" / "lists")
.and(warp::path::end())
.and(warp::get())
.and(accept_json)
.map(|| warp::reply::json(&super::get_lists()))
.with(&cors)
.recover(handle_rejection);
let list = warp::path!("v1" / "lists" / String)
.and(warp::path::end())
.and(warp::get())
.and(accept_json)
.and_then(|id: String| async move {
match Uuid::parse_str(&id) {
Ok(uuid) => {
let list = &super::get_list(uuid);
match list {
Some(l) => Ok(warp::reply::json(l)),
None => Err(warp::reject::not_found()),
}
}
Err(e) => Err(warp::reject::custom(InvalidUuid)),
}
})
.with(&cors)
.recover(handle_rejection);
let routes = root.or(v1).or(lists).or(list).boxed();
routes
}
// See https://github.com/seanmonstar/warp/blob/master/examples/rejections.rs
async fn handle_rejection(err: warp::Rejection) -> Result<impl warp::Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
message = "NOT_FOUND";
code = StatusCode::NOT_FOUND;
} else if let Some(InvalidUuid) = err.find() {
code = StatusCode::BAD_REQUEST;
message = "INVALID_UUID";
} else if let Some(e) = err.find::<warp::filters::body::BodyDeserializeError>() {
message = "BAD_REQUEST";
code = StatusCode::BAD_REQUEST;
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
message = "METHOD_NOT_ALLOWED";
code = StatusCode::METHOD_NOT_ALLOWED;
} else {
// We should have expected this... Just log and say its a 500
eprintln!("unhandled rejection: {:?}", err);
message = "UNHANDLED_REJECTION";
code = StatusCode::INTERNAL_SERVER_ERROR;
}
let json = warp::reply::json(&ErrorMessage {
success: false,
code: code.as_u16(),
message: message.into(),
});
Ok(warp::reply::with_status(json, code))
}