Move filters into main.rs for SPEED

Using a macro from the warp github to balance the OR filter tree which sped up compile times by 10x.
This commit is contained in:
2020-10-20 21:50:01 -04:00
parent 0dc4247224
commit 3f124ce439
6 changed files with 336 additions and 419 deletions

View File

@@ -1,268 +0,0 @@
use http::StatusCode;
use serde::de::DeserializeOwned;
use std::convert::Infallible;
use warp::{Filter, Rejection, Reply};
use super::handlers;
use super::models::{InteriorRefList, ListParams, MerchandiseList, Owner, Shop};
use super::Environment;
pub fn status() -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::path("status")
.and(warp::get())
.map(|| StatusCode::OK) // TODO: return what api versions this server supports instead
}
pub fn shops(env: Environment) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path("shops").and(
get_shop(env.clone())
.or(delete_shop(env.clone()))
.or(update_shop(env.clone()))
.or(create_shop(env.clone()))
.or(list_shops(env.clone()))
.or(get_latest_interior_ref_list_by_shop_id(env)),
)
}
pub fn owners(env: Environment) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path("owners").and(
get_owner(env.clone())
.or(delete_owner(env.clone()))
.or(update_owner(env.clone()))
.or(create_owner(env.clone()))
.or(list_owners(env)),
)
}
pub fn interior_ref_lists(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path("interior_ref_lists").and(
get_interior_ref_list(env.clone())
.or(delete_interior_ref_list(env.clone()))
.or(create_interior_ref_list(env.clone()))
.or(list_interior_ref_lists(env)),
)
}
pub fn merchandise_lists(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path("merchandise_lists").and(
get_merchandise_list(env.clone())
.or(delete_merchandise_list(env.clone()))
.or(create_merchandise_list(env.clone()))
.or(list_merchandise_lists(env)),
)
}
pub fn get_shop(env: Environment) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env))
.and_then(handlers::get_shop)
}
pub fn create_shop(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::post())
.and(json_body::<Shop>())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::create_shop)
}
pub fn delete_shop(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::delete_shop)
}
pub fn update_shop(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::patch())
.and(json_body::<Shop>())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::update_shop)
}
pub fn list_shops(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env))
.and_then(handlers::list_shops)
}
pub fn get_owner(env: Environment) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env))
.and_then(handlers::get_owner)
}
pub fn create_owner(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::post())
.and(json_body::<Owner>())
.and(warp::addr::remote())
.and(warp::header::optional("api-key"))
.and(warp::header::optional("x-real-ip"))
.and(with_env(env))
.and_then(handlers::create_owner)
}
pub fn delete_owner(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::delete_owner)
}
pub fn update_owner(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::patch())
.and(json_body::<Owner>())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::update_owner)
}
pub fn list_owners(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env))
.and_then(handlers::list_owners)
}
pub fn get_interior_ref_list(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env))
.and_then(handlers::get_interior_ref_list)
}
pub fn create_interior_ref_list(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::post())
.and(json_body::<InteriorRefList>())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::create_interior_ref_list)
}
pub fn delete_interior_ref_list(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::delete_interior_ref_list)
}
pub fn list_interior_ref_lists(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env))
.and_then(handlers::list_interior_ref_lists)
}
pub fn get_latest_interior_ref_list_by_shop_id(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path("latest_interior_ref_list"))
.and(warp::path::end())
.and(warp::get())
.and(with_env(env))
.and_then(handlers::get_latest_interior_ref_list_by_shop_id)
}
pub fn get_merchandise_list(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env))
.and_then(handlers::get_merchandise_list)
}
pub fn create_merchandise_list(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::post())
.and(json_body::<MerchandiseList>())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::create_merchandise_list)
}
pub fn delete_merchandise_list(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env))
.and_then(handlers::delete_merchandise_list)
}
pub fn list_merchandise_lists(
env: Environment,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env))
.and_then(handlers::list_merchandise_lists)
}
fn with_env(env: Environment) -> impl Filter<Extract = (Environment,), Error = Infallible> + Clone {
warp::any().map(move || env.clone())
}
fn json_body<T>() -> impl Filter<Extract = (T,), Error = warp::Rejection> + Clone
where
T: Send + DeserializeOwned,
{
warp::body::content_length_limit(1024 * 64).and(warp::body::json())
}

70
src/macros.rs Normal file
View File

@@ -0,0 +1,70 @@
// From SafariMonkey and jhpratt in https://github.com/seanmonstar/warp/issues/619
// I'm including this to reduce huge compile times until rustc is fixed.
/// Takes a list of handler expressions and `or`s them together
/// in a balanced tree. That is, instead of `a.or(b).or(c).or(d)`,
/// it produces `(a.or(b)).or(c.or(d))`, thus nesting the types
/// less deeply, which provides improvements in compile time.
///
/// It also applies `::warp::Filter::boxed` to each handler expression
/// when in `debug_assertions` mode, improving compile time further.
//
// The basic list splitting algorithm here is based on this gist:
// https://gist.github.com/durka/9fc479de2555225a787f
// It uses a counter from which two items are removed each time,
// stopping when the counter reaches 0. At each step, one item
// is moved from the left to the right, and thus at the end,
// there will be the same number of items in each list.
//
// The flow is as follows:
// - If there is one handler expression, debug_box it and return.
// - If there is more than one handler expression:
// - First, copy the list into two: the one that will go into the
// right side of the `or`, and one that will serve as a counter.
// Recurse with these separated by semicolons, plus an empty `left`
// list before the first semicolon.
// - Then, as long as there are at least two items in the counter
// list, remove them and move the first item on the right side of
// the first semicolon (`head`) to the left side of the first semicolon.
// - Finally, when there are one or zero items left in the counter,
// move one last item to the left, make the call this macro on both the
// left and right sides, and `or` the two sides together.
//
// For example, balanced_or_tree!(a, b, c, d, e) would take the following steps:
//
// - balanced_or_tree!(a, b, c, d, e)
// - balanced_or_tree!(@internal ; a, b, c, d, e ; a, b, c, d, e) // initialise lists
// - balanced_or_tree!(@internal a ; b, c, d, e ; c, d, e) // move one elem; remove two
// - balanced_or_tree!(@internal a, b ; c, d, e ; e) // now only one elem in counter
// - balanced_or_tree!(a, b, c).or(balanced_or_tree(d, e)) // recurse on each sublist
macro_rules! balanced_or_tree {
// Base case: just a single expression, return it wrapped in `debug_boxed`
($x:expr $(,)?) => { debug_boxed!($x) };
// Multiple expressions: recurse with three lists: left, right and counter.
($($x:expr),+ $(,)?) => {
balanced_or_tree!(@internal ; $($x),+; $($x),+)
// ^ left ^ right ^ counter
};
// Counter 1 or 2; move one more item and recurse on each sublist, and or them together
(@internal $($left:expr),*; $head:expr, $($tail:expr),+; $a:expr $(,$b:expr)?) => {
(balanced_or_tree!($($left,)* $head)).or(balanced_or_tree!($($tail),+))
};
// Counter > 2; move one item from the right to the left and subtract two from the counter
(@internal $($left:expr),*; $head:expr, $($tail:expr),+; $a:expr, $b:expr, $($more:expr),+) => {
balanced_or_tree!(@internal $($left,)* $head; $($tail),+; $($more),+)
};
}
#[cfg(debug_assertions)]
macro_rules! debug_boxed {
($x:expr) => {
::warp::Filter::boxed($x)
};
}
#[cfg(not(debug_assertions))]
macro_rules! debug_boxed {
($x:expr) => {
$x
};
}

View File

@@ -1,9 +1,10 @@
use anyhow::Result;
use clap::Clap;
use dotenv::dotenv;
use http::StatusCode;
use hyper::server::Server;
use listenfd::ListenFd;
use serde::Serialize;
use serde::{de::DeserializeOwned, Serialize};
use sqlx::postgres::PgPool;
use std::convert::Infallible;
use std::env;
@@ -14,12 +15,18 @@ use warp::Filter;
mod caches;
mod db;
mod filters;
mod handlers;
#[macro_use]
mod macros;
mod models;
mod problem;
use caches::Caches;
use models::interior_ref_list::InteriorRefList;
use models::merchandise_list::MerchandiseList;
use models::owner::Owner;
use models::shop::Shop;
use models::ListParams;
#[derive(Clap)]
#[clap(version = "0.1.0", author = "Tyler Hallada <tyler@hallada.net>")]
@@ -54,6 +61,16 @@ struct ErrorMessage {
message: String,
}
fn with_env(env: Environment) -> impl Filter<Extract = (Environment,), Error = Infallible> + Clone {
warp::any().map(move || env.clone())
}
fn json_body<T>() -> impl Filter<Extract = (T,), Error = warp::Rejection> + Clone
where
T: Send + DeserializeOwned,
{
warp::body::content_length_limit(1024 * 64).and(warp::body::json())
}
#[tokio::main]
async fn main() -> Result<()> {
dotenv().ok();
@@ -76,13 +93,180 @@ async fn main() -> Result<()> {
let api_url = host_url.join("/v1/")?;
let env = Environment::new(api_url).await?;
let base = warp::path("v1");
let routes = filters::status()
.or(base.and(
filters::shops(env.clone())
.or(filters::owners(env.clone()))
.or(filters::interior_ref_lists(env.clone()))
.or(filters::merchandise_lists(env.clone())),
let status_handler = warp::path::path("status")
.and(warp::path::end())
.and(warp::get())
.map(|| StatusCode::OK); // TODO: return what api versions this server supports instead
let get_owner_handler = warp::path("owners").and(
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env.clone()))
.and_then(handlers::get_owner),
);
let create_owner_handler = warp::path("owners").and(
warp::path::end()
.and(warp::post())
.and(json_body::<Owner>())
.and(warp::addr::remote())
.and(warp::header::optional("api-key"))
.and(warp::header::optional("x-real-ip"))
.and(with_env(env.clone()))
.and_then(handlers::create_owner),
);
let delete_owner_handler = warp::path("owners").and(
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::delete_owner),
);
let update_owner_handler = warp::path("owners").and(
warp::path::param()
.and(warp::path::end())
.and(warp::patch())
.and(json_body::<Owner>())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::update_owner),
);
let list_owners_handler = warp::path("owners").and(
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env.clone()))
.and_then(handlers::list_owners),
);
let get_shop_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env.clone()))
.and_then(handlers::get_shop),
);
let create_shop_handler = warp::path("shops").and(
warp::path::end()
.and(warp::post())
.and(json_body::<Shop>())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::create_shop),
);
let delete_shop_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::delete_shop),
);
let update_shop_handler = warp::path("shops").and(
warp::path::param()
.and(warp::patch())
.and(json_body::<Shop>())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::update_shop),
);
let list_shops_handler = warp::path("shops").and(
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env.clone()))
.and_then(handlers::list_shops),
);
let get_interior_ref_list_handler = warp::path("interior_ref_lists").and(
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env.clone()))
.and_then(handlers::get_interior_ref_list),
);
let create_interior_ref_list_handler = warp::path("interior_ref_lists").and(
warp::path::end()
.and(warp::post())
.and(json_body::<InteriorRefList>())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::create_interior_ref_list),
);
let delete_interior_ref_list_handler = warp::path("interior_ref_lists").and(
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::delete_interior_ref_list),
);
let list_interior_ref_lists_handler = warp::path("interior_ref_lists").and(
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env.clone()))
.and_then(handlers::list_interior_ref_lists),
);
let get_latest_interior_ref_list_by_shop_id_handler = warp::path("shops").and(
warp::path::param()
.and(warp::path("latest_interior_ref_list"))
.and(warp::path::end())
.and(warp::get())
.and(with_env(env.clone()))
.and_then(handlers::get_latest_interior_ref_list_by_shop_id),
);
let get_merchandise_list_handler = warp::path("merchandise_lists").and(
warp::path::param()
.and(warp::path::end())
.and(warp::get())
.and(with_env(env.clone()))
.and_then(handlers::get_merchandise_list),
);
let create_merchandise_list_handler = warp::path("merchandise_lists").and(
warp::path::end()
.and(warp::post())
.and(json_body::<MerchandiseList>())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::create_merchandise_list),
);
let delete_merchandise_list_handler = warp::path("merchandise_lists").and(
warp::path::param()
.and(warp::path::end())
.and(warp::delete())
.and(warp::header::optional("api-key"))
.and(with_env(env.clone()))
.and_then(handlers::delete_merchandise_list),
);
let list_merchandise_lists_handler = warp::path("merchandise_lists").and(
warp::path::end()
.and(warp::get())
.and(warp::query::<ListParams>())
.and(with_env(env.clone()))
.and_then(handlers::list_merchandise_lists),
);
let routes = warp::path("v1")
.and(balanced_or_tree!(
status_handler,
get_owner_handler,
delete_owner_handler,
update_owner_handler,
create_owner_handler,
list_owners_handler,
get_shop_handler,
delete_shop_handler,
update_shop_handler,
create_shop_handler,
list_shops_handler,
get_latest_interior_ref_list_by_shop_id_handler,
get_interior_ref_list_handler,
delete_interior_ref_list_handler,
create_interior_ref_list_handler,
list_interior_ref_lists_handler,
get_merchandise_list_handler,
delete_merchandise_list_handler,
create_merchandise_list_handler,
list_merchandise_lists_handler,
))
.recover(problem::unpack_problem)
.with(warp::compression::gzip())

View File

@@ -7,7 +7,6 @@ use tracing::instrument;
use super::ListParams;
use super::{Model, UpdateableModel};
use crate::models::InteriorRefList;
use crate::problem::forbidden_permission;
#[derive(Debug, Serialize, Deserialize, Clone)]