167 lines
4.2 KiB
Rust
167 lines
4.2 KiB
Rust
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<Page>,
|
|
env: Environment<'a>,
|
|
}
|
|
|
|
/// Serves a request and returns a response.
|
|
async fn serve(req: Request<Incoming>, state: Arc<AppState<'_>>) -> Result<Response<Full<Bytes>>> {
|
|
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<TcpStream>, state: Arc<AppState<'_>>) -> 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<Executor<'static>>,
|
|
listener: Async<TcpListener>,
|
|
state: Arc<AppState<'static>>,
|
|
) -> 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<AppState<'a>> {
|
|
let mut env = Environment::new();
|
|
let mut pages: Vec<Page> = 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<Executor<'static>>) -> Result<()> {
|
|
let mut state = init().await?;
|
|
|
|
let _ = listen(
|
|
ex,
|
|
Async::<TcpListener>::bind(([127, 0, 0, 1], 8000))?,
|
|
Arc::new(state),
|
|
)
|
|
.await;
|
|
Ok(())
|
|
}
|
|
|
|
/// A TCP or TCP+TLS connection.
|
|
enum SmolStream {
|
|
Plain(Async<TcpStream>),
|
|
}
|
|
|
|
impl AsyncRead for SmolStream {
|
|
fn poll_read(
|
|
mut self: Pin<&mut Self>,
|
|
cx: &mut Context<'_>,
|
|
buf: &mut [u8],
|
|
) -> Poll<io::Result<usize>> {
|
|
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<io::Result<usize>> {
|
|
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<io::Result<()>> {
|
|
match &mut *self {
|
|
Self::Plain(s) => Pin::new(s).poll_close(cx),
|
|
}
|
|
}
|
|
|
|
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
match &mut *self {
|
|
Self::Plain(s) => Pin::new(s).poll_close(cx),
|
|
}
|
|
}
|
|
}
|