use std::net::{TcpListener, TcpStream}; use std::pin::Pin; use std::string; use std::sync::Arc; use std::task::{Context, Poll}; use anyhow::Result; use bytes::Bytes; use http_body_util::Full; use hyper::body::Incoming; use hyper::service::service_fn; use hyper::{Request, Response}; use macro_rules_attribute::apply; use minijinja::{context, Environment}; use smol::{fs, io, prelude::*, Async, Executor}; use smol_hyper::rt::{FuturesIo, SmolTimer}; use smol_macros::main; mod assets; mod loader; mod page; use crate::assets::AssetStore; use crate::loader::*; use crate::page::*; struct AppState<'a> { pub pages: Vec, env: Environment<'a>, } /// Serves a request and returns a response. async fn serve(req: Request, state: Arc>) -> Result>> { println!("Serving {}", req.uri()); let path = req.uri().path(); let mreow = state.pages.iter().find(|&x| { if let Some(path_x) = &x.header.path { println!("{},{}", path_x, path); if path_x == path { true } else { false } } else { false } }); let reply: Bytes = match mreow { Some(x) => state .env .get_template(&x.header.id) .unwrap() .render(context! {}) .unwrap() .into(), None => match AssetStore::new("./templateServe/_assets/".into()) .load_asset(path) .await { Ok(asset) => asset, Err(_) => "".into(), }, }; Ok(Response::new(Full::new(reply.into()))) } async fn handle_client(client: Async, state: Arc>) -> Result<()> { let client = SmolStream::Plain(client); hyper::server::conn::http1::Builder::new() .timer(SmolTimer::new()) .serve_connection( FuturesIo::new(client), service_fn(move |req| serve(req, state.clone())), ) .await?; Ok(()) } /// Listens for incoming connections and serves them. async fn listen( ex: &Arc>, listener: Async, state: Arc>, ) -> Result<()> { loop { // Wait for a new client. let (client, _) = listener.accept().await?; let cloned_state = state.clone(); // Spawn a task to handle this connection. ex.spawn({ async move { if let Err(e) = handle_client(client, cloned_state).await { println!("Error while handling client: {}", e); } } }) .detach(); } } async fn init<'a>() -> Result> { let mut env = Environment::new(); let mut pages: Vec = Vec::new(); env.add_global("globals", context! {version => "0.1.0"}); let mut state: AppState = AppState { pages, env }; state.load_from_fs().await?; Ok(state) } #[apply(main!)] async fn main(ex: &Arc>) -> Result<()> { let mut state = init().await?; let _ = listen( ex, Async::::bind(([127, 0, 0, 1], 8000))?, Arc::new(state), ) .await; Ok(()) } /// A TCP or TCP+TLS connection. enum SmolStream { Plain(Async), } impl AsyncRead for SmolStream { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_read(cx, buf), } } } impl AsyncWrite for SmolStream { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_write(cx, buf), } } fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_close(cx), } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { match &mut *self { Self::Plain(s) => Pin::new(s).poll_close(cx), } } }