diff --git a/Cargo.lock b/Cargo.lock index 9009c1e..89d1244 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -757,7 +757,9 @@ dependencies = [ "futures", "minijinja", "notify", + "pathdiff", "redact", + "serde", "sled", "tokio", "tower", @@ -765,6 +767,7 @@ dependencies = [ "tower-livereload", "tracing", "tracing-subscriber", + "walkdir", ] [[package]] @@ -1232,18 +1235,18 @@ checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "serde" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index b1bc9d5..a6d93d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,9 @@ config = "0.14.0" futures = "0.3.30" minijinja = { version = "2.0.1", features = ["loader"] } notify = "6.1.1" +pathdiff = "0.2.1" redact = { version = "0.1.9", features = ["serde"] } +serde = "1.0.201" sled = "0.34.7" tokio = { version = "1.37.0", features = ["full"] } tower = "0.4.13" @@ -20,3 +22,4 @@ tower-http = { version = "0.5.2", features = ["fs"] } tower-livereload = "0.9.2" tracing = "0.1.40" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +walkdir = "2.5.0" diff --git a/flake.nix b/flake.nix index 1c3eb57..527cccd 100644 --- a/flake.nix +++ b/flake.nix @@ -28,6 +28,7 @@ pkg-config inotify-tools tailwindcss + nodePackages.typescript-language-server ]; }; diff --git a/pages/index.html.jinja b/pages/index.html.jinja deleted file mode 100644 index afa4658..0000000 --- a/pages/index.html.jinja +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "page.html.jinja" %} -{% block body %} -

Index

-

- Welcome to my awesome homepage. -

-{% endblock %} diff --git a/src/main.rs b/src/main.rs index 130140b..348c07e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,14 +2,11 @@ use axum::extract::State; use axum::response::IntoResponse; use axum::routing::get; use axum::{http::StatusCode, response::Html, serve, Router}; -use minijinja::{context, Environment}; +use minijinja::context; use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; -use std::process::Stdio; -use std::sync::Arc; use std::{path::PathBuf, str::FromStr, sync::OnceLock}; -use tokio::io::{AsyncBufReadExt, BufReader}; +use templates::Templates; use tokio::sync::mpsc::channel; -use tokio::sync::Mutex; use tower_http::services::ServeDir; use tower_livereload::{LiveReloadLayer, Reloader}; pub use tracing::{debug, error, event, info, span, warn, Level}; @@ -18,19 +15,9 @@ pub use tracing::{debug, error, event, info, span, warn, Level}; struct Berr(Box); type Besult = std::result::Result; -mod observe { - pub fn setup_logging() { - color_eyre::install().expect("Failed to install color_eyre"); - - let filter = tracing_subscriber::EnvFilter::builder() - .with_default_directive(::from( - tracing::level_filters::LevelFilter::TRACE, - )) - .parse_lossy("info,lyrs=trace"); - - tracing_subscriber::fmt().with_env_filter(filter).init(); - } -} +mod observe; +mod tailwind; +mod templates; fn static_file_dir() -> &'static PathBuf { static STATIC_FILE_DIR: OnceLock = OnceLock::new(); @@ -39,53 +26,22 @@ fn static_file_dir() -> &'static PathBuf { #[derive(Clone)] struct AppState { - templates: Arc>>, + templates: Templates, } #[tokio::main] async fn main() -> Besult<()> { // load configuration? - observe::setup_logging(); + let _setup_logging = observe::setup_logging(); + + // TODO: reload templates when they change? separate watcher? + let templates = Templates::try_load().await?; + let mut tt = templates.clone(); + let templates_watcher = tt.start_watcher(); // TODO: only start tailwind if in dev mode? - tokio::spawn(async move { - info!("Starting tailwind..."); - match tokio::process::Command::new("tailwindcss") - .args(["-i", "src/style.css", "-o", "static/style.css", "--watch"]) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - { - Ok(mut tw) => { - info!("Tailwind spawned!"); - let mut stdout_reader = BufReader::new(tw.stdout.take().unwrap()).lines(); - tokio::spawn(async move { - while let Ok(Some(l)) = stdout_reader.next_line().await { - if l.trim().len() > 0 { - event!(target: "tailwind::stdout", Level::INFO, "{l}"); - } - } - }); - let mut stderr_reader = BufReader::new(tw.stderr.take().unwrap()).lines(); - tokio::spawn(async move { - while let Ok(Some(l)) = stderr_reader.next_line().await { - if l.trim().len() > 0 { - event!(target: "tailwind::stderr", Level::INFO, "{l}"); - } - } - }); - } - Err(e) => error!("Failed to spawn Tailwind: {e}"), - } - }); - - let templates = Arc::new(Mutex::new(Environment::new())); - while let Some(d) = tokio::fs::read_dir("templates").await?.next_entry().await? { - templates.clone().lock().await.add_template_owned( - d.file_name().into_string().unwrap(), - std::fs::read_to_string(d.path())?, - )?; - } + tokio::spawn(async { tailwind::start_watcher() }); + tokio::spawn(async move { templates_watcher.await }); // pulling the watcher into main's scope lets it live until the program dies let (rl_layer, _watcher) = live_reload_layer()?; @@ -105,10 +61,8 @@ async fn index(State(state): State) -> Besult> { Ok(Html( state .templates - .lock() - .await - .get_template("page.html")? - .render(context!())?, + .render("pages/index.html.jinja", context!()) + .await?, )) } @@ -142,9 +96,7 @@ fn static_file_watcher( watcher.watch(static_file_dir(), RecursiveMode::Recursive)?; tokio::spawn(async move { - info!("Recieving..."); while let Some(res) = rx.recv().await { - info!("Recieved! {res:#?}"); match res { Ok(event) => { info!("fs event: {event:#?}"); diff --git a/src/observe.rs b/src/observe.rs new file mode 100644 index 0000000..6e10124 --- /dev/null +++ b/src/observe.rs @@ -0,0 +1,11 @@ +pub fn setup_logging() { + color_eyre::install().expect("Failed to install color_eyre"); + + let filter = tracing_subscriber::EnvFilter::builder() + .with_default_directive(::from( + tracing::level_filters::LevelFilter::TRACE, + )) + .parse_lossy("info,lyrs=trace"); + + tracing_subscriber::fmt().with_env_filter(filter).init(); +} diff --git a/src/tailwind.rs b/src/tailwind.rs new file mode 100644 index 0000000..23080d3 --- /dev/null +++ b/src/tailwind.rs @@ -0,0 +1,37 @@ +use std::process::Stdio; +use tokio::{ + io::{AsyncBufReadExt, BufReader}, + process::Command, +}; +use tracing::{error, event, info, Level}; + +pub fn start_watcher() { + info!("Starting tailwind..."); + match Command::new("tailwindcss") + .args(["-i", "src/style.css", "-o", "static/style.css", "--watch"]) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(mut tw) => { + info!("Tailwind spawned!"); + let mut stdout_reader = BufReader::new(tw.stdout.take().unwrap()).lines(); + tokio::spawn(async move { + while let Ok(Some(l)) = stdout_reader.next_line().await { + if l.trim().len() > 0 { + event!(target: "tailwind::stdout", Level::INFO, "{l}"); + } + } + }); + let mut stderr_reader = BufReader::new(tw.stderr.take().unwrap()).lines(); + tokio::spawn(async move { + while let Ok(Some(l)) = stderr_reader.next_line().await { + if l.trim().len() > 0 { + event!(target: "tailwind::stderr", Level::INFO, "{l}"); + } + } + }); + } + Err(e) => error!("Failed to spawn Tailwind: {e}"), + } +} diff --git a/src/templates.rs b/src/templates.rs new file mode 100644 index 0000000..b1b0c03 --- /dev/null +++ b/src/templates.rs @@ -0,0 +1,68 @@ +use std::sync::Arc; + +use minijinja::Environment; +use pathdiff::diff_paths; +use tokio::sync::Mutex; +use tracing::info; + +#[derive(Clone)] +pub struct Templates { + env: Arc>>, +} + +pub type Error = Box; + +impl Templates { + pub fn empty() -> Self { + let env = Arc::new(Mutex::new(Environment::new())); + Self { env } + } + + pub async fn try_load() -> Result { + let mut result = Self::empty(); + result.load_env().await?; + Ok(result) + } + + pub async fn start_watcher(&mut self) { + info!("TODO: Implement template watcher"); + } + + pub async fn load_env(&mut self) -> Result<(), Error> { + info!("Loading templates..."); + for d in walkdir::WalkDir::new("src/templates") { + match d { + Ok(d) => { + if d.file_type().is_dir() { + continue; + } + let filename: String = diff_paths(d.path(), "src/templates") + .unwrap() + .to_string_lossy() + .into_owned(); + info!("Loading template {filename:#?} ({d:#?})"); + self.env + .clone() + .lock() + .await + .add_template_owned(filename, std::fs::read_to_string(d.path())?)?; + } + Err(_) => todo!(), + } + } + info!("Done loading templates!"); + Ok(()) + } + + pub async fn render( + &self, + template_name: &str, + context: S, + ) -> Result { + self.env + .lock() + .await + .get_template(template_name)? + .render(context) + } +} diff --git a/templates/page.html.jinja b/src/templates/page.html.jinja similarity index 100% rename from templates/page.html.jinja rename to src/templates/page.html.jinja diff --git a/src/templates/pages/index.html.jinja b/src/templates/pages/index.html.jinja new file mode 100644 index 0000000..796b34b --- /dev/null +++ b/src/templates/pages/index.html.jinja @@ -0,0 +1,7 @@ +{% extends "page.html.jinja" %} +{% block body %} +

Index

+

+ Welcome to my awesome homepage! +

+{% endblock %} diff --git a/tailwind.config.js b/tailwind.config.js index fa9e25e..fdc6d81 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,38 +1,35 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ["./src/**/*.rs","./static/**/*.{html,js}"], theme: { + content: ["./src/**/*"], theme: { colors: { - - rosewater: "var(--Rosewater)", - flamingo: "var(--Flamingo)", - pink: "var(--Pink)", - mauve: "var(--Mauve)", - red: "var(--Red)", - maroon: "var(--Maroon)", - peach: "var(--Peach)", - yellow: "var(--Yellow)", - green: "var(--Green)", - teal: "var(--Teal)", - sky: "var(--Sky)", - sapphire: "var(--Sapphire)", - blue: "var(--Blue)", - lavender: "var(--Lavender)", - text: "var(--Text)", - subtext1: "var(--Subtext1)", - subtext0: "var(--Subtext0)", - overlay2: "var(--Overlay2)", - overlay1: "var(--Overlay1)", - overlay0: "var(--Overlay0)", - surface2: "var(--Surface2)", - surface1: "var(--Surface1)", - surface0: "var(--Surface0)", - base: "var(--Base)", - mantle: "var(--Mantle)", - crust: "var(--Crust)", - + rosewater: "var(--Rosewater)", + flamingo: "var(--Flamingo)", + pink: "var(--Pink)", + mauve: "var(--Mauve)", + red: "var(--Red)", + maroon: "var(--Maroon)", + peach: "var(--Peach)", + yellow: "var(--Yellow)", + green: "var(--Green)", + teal: "var(--Teal)", + sky: "var(--Sky)", + sapphire: "var(--Sapphire)", + blue: "var(--Blue)", + lavender: "var(--Lavender)", + text: "var(--Text)", + subtext1: "var(--Subtext1)", + subtext0: "var(--Subtext0)", + overlay2: "var(--Overlay2)", + overlay1: "var(--Overlay1)", + overlay0: "var(--Overlay0)", + surface2: "var(--Surface2)", + surface1: "var(--Surface1)", + surface0: "var(--Surface0)", + base: "var(--Base)", + mantle: "var(--Mantle)", + crust: "var(--Crust)", }, extend: { - }, }, plugins: [],