diff --git a/Cargo.lock b/Cargo.lock index 994377c..ae9cfaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -541,6 +541,15 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_spanned" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +dependencies = [ + "serde_core", +] + [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -622,6 +631,7 @@ name = "templateServe" version = "0.1.0" dependencies = [ "anyhow", + "bytes", "http-body-util", "hyper", "macro_rules_attribute", @@ -630,6 +640,7 @@ dependencies = [ "smol", "smol-hyper", "smol-macros", + "toml", ] [[package]] @@ -655,6 +666,45 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.9.11+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" + [[package]] name = "tracing" version = "0.1.44" @@ -694,3 +744,9 @@ checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] + +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" diff --git a/Cargo.toml b/Cargo.toml index c3a7f30..31f934e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] anyhow = "1.0.100" +bytes = "1.11.0" http-body-util = "0.1.3" hyper = { version = "1.8.1", features = ["http1", "http2", "server"] } macro_rules_attribute = "0.2.2" @@ -15,3 +16,4 @@ serde = { version = "1.0.228", features = ["derive"] } smol = "2.0.2" smol-hyper = "0.1.1" smol-macros = "0.1.1" +toml = "0.9.11" diff --git a/bleh.txt b/bleh.txt new file mode 100644 index 0000000..f72786b --- /dev/null +++ b/bleh.txt @@ -0,0 +1,44 @@ +- templete-serve + - config.toml + - home + - id: "MAINHOME" + - type: root + - assets + - pngs & mp4's + - base + - config.toml + - id: "base" + - title: "" + - type: page + - path: Empty, Not meant to be visited + - comment: "Base page that others can extend from" + - include: [style.css] // <- FEATURE FOR LATER + - extras + - index.html + - home + - config.toml + - id: "mainHome" + - title: "home" + - type: page + - path: "/" + - extras + - web stamps + - extra data yk + - index.html + - second + - config.toml + - id: "secondPage" + - title: "bleh" + - type: page + - path: "/" + - extras + - web stamps + - extra data yk + - index.html + - 404 + - config.toml + - id: "404page" + - title: "home" + - type: page + - path: "/404" + - index.html diff --git a/src/loader.rs b/src/loader.rs new file mode 100644 index 0000000..dcf0d1b --- /dev/null +++ b/src/loader.rs @@ -0,0 +1,83 @@ +use std::future::IntoFuture; + +use crate::AppState; +use anyhow::anyhow; +use minijinja::Value; +use serde::Deserialize; +use smol::{ + fs::{self, DirEntry}, + stream::StreamExt, +}; +use toml::Table; + +pub struct Page { + header: Header, + content: String, +} + +#[derive(Deserialize, Debug)] +pub enum PageType { + // If root, the dir contains all the pages, if there are no dirs within the root, we can assume + // its empty + Root, + // Contains a single html + Page, + // Is template + Base, +} + +#[derive(Deserialize, Debug)] +pub struct Header { + id: String, + // r#type: PageType, + r#type: String, + title: Option, + path: Option, + // TODO impl this later + // extras: Table, +} +#[derive(Deserialize, Debug)] +pub struct Root { + id: String, + // r#type: PageType, + r#type: String, + title: Option, + path: Option, + // TODO impl this later + // extras: Table, +} + +impl AppState<'_> { + pub async fn load_from_fs(&mut self) -> anyhow::Result<()> { + let mut dir = fs::read_dir("./template-serve").await?; + + // Find config.toml from within dir + let config = dir + .find(|x| x.as_ref().unwrap().file_name().eq("config.toml")) + .await + .unwrap()?; + + while let Some(Ok(entry)) = dir.next().await { + if entry.file_type().await?.is_dir() { + let entry_opened = fs::read_dir(entry.path()).await?; + println!("Opened {}", entry.file_name().to_str().unwrap()); + } else { + println!("Not parsing {}", entry.file_name().to_str().unwrap()); + } + } + + let config_file = fs::read(config.path()).await?; + + let config_parsed: Root = toml::from_slice(config_file.as_slice())?; + + Ok(()) + } +} + +// impl From for Value { +// fn from(value: Table) -> Self { +// Value { +// ..Default::default() +// } +// } +// } diff --git a/src/main.rs b/src/main.rs index ca0a7a3..fbff61c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,56 +1,78 @@ 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, Uri}; +use hyper::{Request, Response}; use macro_rules_attribute::apply; -use smol::{future, io, prelude::*, Async, Executor}; +use minijinja::{context, Environment}; +use smol::{fs, io, prelude::*, Async, Executor}; use smol_hyper::rt::{FuturesIo, SmolTimer}; use smol_macros::main; -/// Serves a request and returns a response. -async fn serve(req: Request) -> Result>> { - println!("Serving {}", req.uri()); - let path = req.uri().path(); - let reply = match path { - "/" => "mreow", - _ => "Invalid path!", - }; - Ok(Response::new(Full::new(reply.as_bytes()))) +mod loader; +use crate::loader::*; + +struct AppState<'a> { + pages: Vec, + env: Environment<'a>, } -/// Handle a new client. -async fn handle_client(client: Async) -> Result<()> { - // Wrap it in TLS if necessary. +/// Serves a request and returns a response. +async fn serve(req: Request, state: Arc>) -> Result>> { + println!("Serving {}", req.uri()); + + let tmpl = state.env.get_template("hello").unwrap(); + let guh = tmpl.render(context!(name => "meowmeowmeowmeow!")).unwrap(); + let guh2 = state.env.get_template("meow").unwrap(); + let guh3 = guh2.render(context!(meow => "meow")).unwrap(); + + let path = req.uri().path(); + let reply: Bytes = match path { + "/" => guh.into(), + "/mreow" => guh3.into(), + _ => "Invalid path!".into(), + }; + Ok(Response::new(Full::new(reply))) +} + +async fn handle_client(client: Async, state: Arc>) -> Result<()> { let client = SmolStream::Plain(client); - // Build the server. hyper::server::conn::http1::Builder::new() .timer(SmolTimer::new()) - .serve_connection(FuturesIo::new(client), service_fn(serve)) + .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) -> Result<()> { +async fn listen( + ex: &Arc>, + listener: Async, + state: Arc>, +) -> Result<()> { // Format the full host address. // println!("Listening on {}", host); 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).await { + if let Err(e) = handle_client(client, cloned_state).await { println!("Error while handling client: {}", e); } } @@ -59,11 +81,25 @@ async fn listen(ex: &Arc>, listener: Async) -> Re } } +async fn init<'a>() -> Result> { + let mut env = Environment::new(); + let mut pages: Vec = Vec::new(); + let mut state: AppState = AppState { pages, env }; + state.load_from_fs().await?; + + Ok(state) +} + #[apply(main!)] async fn main(ex: &Arc>) -> Result<()> { - // Start HTTP and HTTPS servers. - let _ = listen(ex, Async::::bind(([127, 0, 0, 1], 8000))?).await; - print!("mroew"); + let mut state = init().await?; + + let _ = listen( + ex, + Async::::bind(([127, 0, 0, 1], 8000))?, + Arc::new(state), + ) + .await; Ok(()) } diff --git a/template-serve/config.toml b/template-serve/config.toml new file mode 100644 index 0000000..1e39ea0 --- /dev/null +++ b/template-serve/config.toml @@ -0,0 +1,2 @@ +id = "mainhome" +type = "Root" diff --git a/template-serve/home/config.toml b/template-serve/home/config.toml new file mode 100644 index 0000000..e69de29