servile-cli 0.1.0

Static file server with live-reload
Documentation
use clap::Parser;
use std::path::PathBuf;

#[derive(Parser, Debug)]
#[command(name = "servile", about = "Serve a directory over HTTP with live-reload")]
pub struct Config {
    #[arg(long, default_value = "127.0.0.1")]
    pub host: String,

    #[arg(long, default_value_t = 8080)]
    pub port: u16,

    #[arg(default_value = ".")]
    pub dir: PathBuf,

    #[arg(long, default_value_t = 200)]
    pub debounce: u64,
}

#[tokio::main]
async fn main() {
    let config = Config::parse();
    let addr = format!("{}:{}", config.host, config.port);

    let app = servile_cli::build_router(&config.dir, config.debounce);

    let listener = tokio::net::TcpListener::bind(&addr)
        .await
        .unwrap_or_else(|e| panic!("failed to bind to {addr}: {e}"));

    eprintln!("serving {} on http://{addr}", config.dir.display());

    axum::serve(listener, app).await.unwrap();
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_defaults() {
        let config = Config::parse_from(["servile"]);
        assert_eq!(config.host, "127.0.0.1");
        assert_eq!(config.port, 8080);
        assert_eq!(config.dir, PathBuf::from("."));
        assert_eq!(config.debounce, 200);
    }

    #[test]
    fn parse_custom_args() {
        let config = Config::parse_from([
            "servile",
            "--host", "0.0.0.0",
            "--port", "3000",
            "--debounce", "500",
            "/tmp/site",
        ]);
        assert_eq!(config.host, "0.0.0.0");
        assert_eq!(config.port, 3000);
        assert_eq!(config.dir, PathBuf::from("/tmp/site"));
        assert_eq!(config.debounce, 500);
    }
}