use std::str::FromStr; use warp::{http::HeaderValue, hyper::Uri, path::Tail, reply::Response, Filter, Rejection, Reply}; use super::servek::{Server, ServerError}; use rust_embed::RustEmbed; #[derive(RustEmbed)] #[folder = "static"] struct StaticData; impl Server { pub(super) async fn html( &self, ) -> impl Filter + Clone { Self::index() .or(self.profile().await) .or(self.create_profile().await.or(Server::static_files())) } fn index() -> impl Filter + Clone { warp::get().and(warp::path::end().map(move || { warp::reply::html(include_str!("../../templates/html/index.html").to_owned()) })) } fn with_server( srv: Server, ) -> impl Filter + Clone { warp::any().map(move || srv.clone()) } async fn profile(&self) -> impl Filter + Clone { warp::get().and( warp::path!("@" / String) .and(Self::with_server(self.clone())) .and_then(|username: String, srv: Server| async move { srv.hb .render( "profile", &serde_json::json!(srv .profiler .profile(username) .await .map_err(|e| ServerError::from(e))?), ) .map(|html| warp::reply::html(html)) .map_err(|e| ServerError::from(e).reject_self()) }), ) } async fn create_profile(&self) -> impl Filter + Clone { warp::post().and( warp::path!("@" / String) .and(Self::with_server(self.clone())) .and_then(|username: String, srv: Server| async move { let user = srv .profiler .create_user(username, None) .await .map_err(|e| ServerError::from(e)); match user { Ok(u) => Ok(warp::redirect( Uri::from_str(format!("/@/{}", u.username).as_str()).unwrap(), )), Err(e) => Err(e.reject_self()), } }), ) } fn static_files() -> impl Filter + Clone { warp::get().and(warp::path("static").and(warp::path::tail()).and_then( |path: Tail| async move { let asset = match StaticData::get(path.as_str()) { Some(a) => a, None => return Err(ServerError::NotFound.reject_self()), }; let mime = mime_guess::from_path(path.as_str()).first_or_octet_stream(); let mut res = Response::new(asset.data.into()); res.headers_mut().insert( "Content-Type", HeaderValue::from_str(mime.as_ref()).unwrap(), ); Ok(res) }, )) } }