112 lines
3.2 KiB
Rust
112 lines
3.2 KiB
Rust
use core::panic;
|
|
use std::{convert::Infallible, fmt::Display};
|
|
|
|
use handlebars::{Handlebars, RenderError};
|
|
use warp::{hyper::StatusCode, reject::Reject, Filter, Rejection, Reply};
|
|
|
|
use crate::{
|
|
model,
|
|
svc::profiles::{Profiler, UserError},
|
|
};
|
|
|
|
#[derive(Clone)]
|
|
pub struct Server {
|
|
pub(super) hb: Handlebars<'static>,
|
|
pub(super) profiler: Profiler,
|
|
}
|
|
|
|
impl Server {
|
|
pub fn new(profiler: Profiler) -> Self {
|
|
let mut hb = Handlebars::new();
|
|
hb.register_template_string("profile", include_str!("../../templates/html/profile.html"))
|
|
.expect("profile template");
|
|
|
|
Self { hb, profiler }
|
|
}
|
|
|
|
pub async fn listen_and_serve(self, port: u16) -> ! {
|
|
println!("starting server on port {}", port);
|
|
warp::serve(self.html().await.recover(Self::handle_rejection))
|
|
.run(([127, 0, 0, 1], port))
|
|
.await;
|
|
panic!("server stopped prematurely")
|
|
}
|
|
|
|
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(err) = err.find::<ServerError>() {
|
|
match err {
|
|
ServerError::Internal(err) => {
|
|
println!("internal server error: {}", err);
|
|
code = StatusCode::INTERNAL_SERVER_ERROR;
|
|
message = "internal server error";
|
|
}
|
|
ServerError::User(u) => match u {
|
|
UserError::Duplicate => {
|
|
code = StatusCode::BAD_REQUEST;
|
|
message = "duplicate entry";
|
|
}
|
|
UserError::Other(_) => {
|
|
panic!("FIXME: other case should already be handled in conversions")
|
|
}
|
|
UserError::NotFound => {
|
|
code = StatusCode::NOT_FOUND;
|
|
message = "not found";
|
|
}
|
|
},
|
|
}
|
|
} else {
|
|
// We should have expected this... Just log and say its a 500
|
|
println!("FIXME: unhandled rejection: {:?}", err);
|
|
code = StatusCode::INTERNAL_SERVER_ERROR;
|
|
message = "internal server error"
|
|
}
|
|
|
|
Ok(warp::reply::with_status(
|
|
warp::reply::json(&model::Error::error(message)),
|
|
code,
|
|
))
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(super) enum ServerError {
|
|
Internal(String),
|
|
User(UserError),
|
|
}
|
|
|
|
impl ServerError {
|
|
pub(super) fn reject_self(self) -> Rejection {
|
|
warp::reject::custom(self)
|
|
}
|
|
}
|
|
|
|
impl Reject for ServerError {}
|
|
|
|
impl From<RenderError> for ServerError {
|
|
fn from(r: RenderError) -> Self {
|
|
Self::Internal(r.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<UserError> for ServerError {
|
|
fn from(u: UserError) -> Self {
|
|
match u {
|
|
UserError::Duplicate => Self::User(u),
|
|
UserError::NotFound => Self::User(u),
|
|
UserError::Other(o) => Self::Internal(format!("UserError: {}", o)),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Display for ServerError {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{}", self)
|
|
}
|
|
}
|