use core::panic; use std::{convert::Infallible, fmt::Display}; use handlebars::{Handlebars, RenderError}; use warp::{ hyper::StatusCode, reject::{MethodNotAllowed, Reject}, Filter, Rejection, Reply, }; use crate::{ model, svc::{ auth::{Auth, AuthError}, profiles::{Profiler, UserError}, }, }; #[derive(Clone)] pub struct Server { pub(super) hb: Handlebars<'static>, pub(super) profiler: Profiler, pub(super) auth: Auth, } impl Server { pub fn new(profiler: Profiler, auth: Auth) -> Self { let mut hb = Handlebars::new(); hb.register_template_string("profile", include_str!("../../templates/html/profile.html")) .expect("profile template"); hb.register_template_string( "login-error", include_str!("../../templates/html/login-error.html"), ) .expect("login-error template"); Self { hb, profiler, auth } } 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 { let code; let message; if err.is_not_found() { code = StatusCode::NOT_FOUND; message = "not found"; } else if let Some(err) = err.find::() { match err { ServerError::Internal(err) => { println!("internal server error: {}", err); code = StatusCode::INTERNAL_SERVER_ERROR; message = "internal server error"; } ServerError::NotFound => { code = StatusCode::NOT_FOUND; message = "not found"; } ServerError::BadRequest(err) => { code = StatusCode::BAD_REQUEST; message = err; } } } else if let Some(err) = err.find::() { println!("MethodNotAllowed: {:#?}", err); 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), NotFound, BadRequest(String), } impl ServerError { pub(super) fn rejection(self) -> Rejection { warp::reject::custom(self) } } impl Reject for ServerError {} impl From for ServerError { fn from(r: RenderError) -> Self { Self::Internal(r.to_string()) } } impl From for ServerError { fn from(u: UserError) -> Self { match u { UserError::Duplicate => Self::BadRequest("duplicate entry exists".to_owned()), UserError::NotFound => Self::NotFound, UserError::Other(o) => Self::Internal(format!("UserError: {}", o)), } } } impl From for ServerError { fn from(a: AuthError) -> Self { match a { AuthError::InvalidCredentials => { ServerError::BadRequest("invalid credentials".to_owned()) } AuthError::ServerError(err) => ServerError::Internal(err), } } } impl Display for ServerError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self) } }