WIP
This commit is contained in:
663
api/src/lib.rs
663
api/src/lib.rs
File diff suppressed because it is too large
Load Diff
128
api/src/main.rs
128
api/src/main.rs
@@ -1,11 +1,5 @@
|
|||||||
use std::convert::Infallible;
|
|
||||||
|
|
||||||
use serde::Serialize;
|
|
||||||
use uuid::Uuid;
|
|
||||||
use warp::http::StatusCode;
|
|
||||||
use warp::Filter;
|
|
||||||
|
|
||||||
use packager;
|
use packager;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum TripItemStatus {
|
enum TripItemStatus {
|
||||||
@@ -95,87 +89,51 @@ impl<'a> Trip<'a> {
|
|||||||
// println!("{:?}", item);
|
// 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]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let accept_json = warp::header::exact("accept", "application/json");
|
// let accept_json = warp::header::exact("accept", "application/json");
|
||||||
let cors = warp::cors().allow_any_origin();
|
// let cors = warp::cors().allow_any_origin();
|
||||||
|
|
||||||
let root = warp::path::end().map(|| "Hi");
|
// let root = warp::path::end()
|
||||||
let v1 = warp::path!("v1")
|
// .map(|| "Hi")
|
||||||
.and(warp::get())
|
// .recover(packager::router::handle_rejection);
|
||||||
.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 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
226
api/src/packagelist.rs
Normal 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
96
api/src/router.rs
Normal 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))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user