#![deny(warnings)]
use rweb::{get, http::StatusCode, reject, Filter, Rejection, Reply};
use serde::Serialize;
use std::{convert::Infallible, num::NonZeroU16};
#[get("/math/{num}")]
fn math(num: u16, #[filter = "div_by"] denom: NonZeroU16) -> impl Reply {
rweb::reply::json(&Math {
op: format!("{} / {}", num, denom),
output: num / denom.get(),
})
}
#[tokio::main]
async fn main() {
let routes = rweb::get().and(math()).recover(handle_rejection);
rweb::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
fn div_by() -> impl Filter<Extract = (NonZeroU16,), Error = Rejection> + Copy {
rweb::header::<u16>("div-by").and_then(|n: u16| async move {
if let Some(denom) = NonZeroU16::new(n) {
Ok(denom)
} else {
Err(reject::custom(DivideByZero))
}
})
}
#[derive(Debug)]
struct DivideByZero;
impl reject::Reject for DivideByZero {}
#[derive(Serialize)]
struct Math {
op: String,
output: u16,
}
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(DivideByZero) = err.find() {
code = StatusCode::BAD_REQUEST;
message = "DIVIDE_BY_ZERO";
} else if err.find::<rweb::reject::MethodNotAllowed>().is_some() {
code = StatusCode::METHOD_NOT_ALLOWED;
message = "METHOD_NOT_ALLOWED";
} else {
eprintln!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "UNHANDLED_REJECTION";
}
let json = rweb::reply::json(&ErrorMessage {
code: code.as_u16(),
message: message.into(),
});
Ok(rweb::reply::with_status(json, code))
}