diff --git a/Cargo.lock b/Cargo.lock index f30c306..da63674 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,7 +154,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c055ee2d014ae5981ce1016374e8213682aa14d9bf40e48ab48b5f3ef20eaa" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn", @@ -236,6 +236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -250,6 +251,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.7.0" @@ -620,6 +633,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 99491cb..fb0ee4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] axum = { version = "0.7.5", features = ["macros", "tokio"] } -clap = "4.5.4" +clap = { version = "4.5.4", features = ["derive"] } color-eyre = "0.6.3" config = "0.14.0" futures = "0.3.30" diff --git a/src/cli.rs b/src/cli.rs index 8b13789..7138072 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1 +1,48 @@ +use crate::prelude::*; +use prelude::*; +mod prelude { + pub use clap::{Args, Parser, Subcommand}; +} + +/// Web application for managing lyrics and live displays +#[derive(Parser)] +#[command(version, about, long_about = None)] +#[command(propagate_version = true)] +pub struct Cli { + #[command(subcommand)] + command: Commands, +} + +#[derive(Subcommand)] +enum Commands { + /// Run the web application server + Run(Run), +} + +/// Doc comment +#[derive(Args)] +struct Run { + /// Doc comment + #[arg(short, long, default_value = None)] + pub watch: bool, +} + +impl Run { + pub async fn run(&self) -> Result<()> { + let (router, _watchers) = crate::router::router(self.watch).await?; + crate::webserver::webserver(router, self.watch).await + } +} + +pub fn cli() -> Result { + Ok(Cli::parse()) +} + +impl Cli { + pub async fn exec(self) -> Result<()> { + match self.command { + Commands::Run(args) => args.run().await, + } + } +} diff --git a/src/main.rs b/src/main.rs index a28b8b9..694fbaf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,6 @@ mod webserver; #[tokio::main] async fn main() -> Result<()> { - // load configuration? let _setup_logging = observe::setup_logging(); - webserver::webserver(router::router().await?).await + cli::cli()?.exec().await } diff --git a/src/router.rs b/src/router.rs index 5222182..6c714e2 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,24 +1,40 @@ -use crate::{prelude::*, state::State as AppState, static_files}; +use crate::{file_watcher::FileWatcher, prelude::*, state::State as AppState, static_files}; use axum::{extract::State, response::Html, routing::get, Router}; use minijinja::context; use tower_livereload::LiveReloadLayer; -pub async fn router() -> Result { +pub async fn router(with_watchers: bool) -> Result<(Router, Vec>)> { let state = AppState::try_new().await?; - let lr = LiveReloadLayer::new(); - let _template_file_watcher = state - .clone() - .templates - .start_watcher(Some(lr.reloader())) - .await?; - let (static_file_service, _static_file_watcher) = static_files::router(Some(lr.reloader()))?; + let live_reload_layer: Option = if with_watchers { + Some(LiveReloadLayer::new()) + } else { + None + }; - Ok(Router::new() + let orl = || { + if let Some(lr) = &live_reload_layer { + Some(lr.reloader()) + } else { + None + } + }; + + let template_file_watcher = state.clone().templates.start_watcher(orl()).await?; + let (static_file_service, static_file_watcher) = static_files::router(orl())?; + + let mut result = Router::new() .route("/", get(index)) .nest_service("/static", static_file_service) - .layer(lr) - .with_state(state)) + .with_state(state.clone()); + + if let Some(lr) = live_reload_layer { + result = result.clone().layer(lr); + } + + let watchers = vec![template_file_watcher, static_file_watcher]; + + Ok((result, watchers)) } async fn index(State(state): State) -> Result> { diff --git a/src/templates.rs b/src/templates.rs index 4a242d4..e460b59 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -4,7 +4,6 @@ use pathdiff::diff_paths; use std::{path::PathBuf, sync::Arc}; use tokio::sync::Mutex; use tower_livereload::Reloader; -use tracing::{info, instrument}; #[derive(Clone, Debug)] pub struct Templates { @@ -51,12 +50,15 @@ impl Templates { Ok(watcher) } - #[instrument] pub async fn load_env(&self) -> Result<()> { info!("Loading templates..."); for d in walkdir::WalkDir::new(&self.dir) { match d { Ok(d) => { + // ignore editor temporary files + if [".bck", ".tmp"].iter().any(|s| d.path().ends_with(s)) { + continue; + } if d.file_type().is_dir() { continue; } diff --git a/src/webserver.rs b/src/webserver.rs index 916c3ad..a7b691f 100644 --- a/src/webserver.rs +++ b/src/webserver.rs @@ -1,9 +1,10 @@ use crate::{prelude::*, tailwind}; use axum::{serve, Router}; -pub async fn webserver(router: Router) -> Result<()> { - // TODO: only start tailwind if in dev mode? - tokio::spawn(async move { tailwind::start_watcher() }); +pub async fn webserver(router: Router, with_watchers: bool) -> Result<()> { + if with_watchers { + tokio::spawn(async move { tailwind::start_watcher() }); + } let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); info!("Listening on {listener:?}");