nitr 0.0.0-beta.1

A Rust web server embedding Lua for fast, efficient and safe dynamic backends.
use anyhow::{bail, Context as _};
use std::path::{Path, PathBuf};

use nitr::{Config, Server};

const DEFAULT_CONFIG_FILE: &str = "nitr.toml";

const USAGE: &str = "\
Usage: nitr [OPTIONS]

Options:
  -c, --config <PATH>  Path to the TOML config file (default: ./nitr.toml)
      --dev            Enable development mode (hot reload)
  -h, --help           Print this help message";

struct Args {
    config: Option<PathBuf>,
    dev: bool,
}

fn parse_args() -> anyhow::Result<Args> {
    let mut args = Args {
        config: None,
        dev: false,
    };
    let mut iter = std::env::args().skip(1);
    while let Some(arg) = iter.next() {
        match arg.as_str() {
            "-c" | "--config" => {
                let path = iter
                    .next()
                    .with_context(|| format!("missing value for {arg}"))?;
                args.config = Some(PathBuf::from(path));
            }
            "--dev" => args.dev = true,
            "-h" | "--help" => {
                println!("{USAGE}");
                std::process::exit(0);
            }
            _ => bail!("unknown argument `{arg}`\n{USAGE}"),
        }
    }
    Ok(args)
}

fn load_config(args: &Args) -> anyhow::Result<Config> {
    let mut cfg = match &args.config {
        Some(path) => Config::from_file(path)?,
        None => {
            let default = Path::new(DEFAULT_CONFIG_FILE);
            if default.is_file() {
                Config::from_file(default)?
            } else {
                Config::default()
            }
        }
    };
    cfg.apply_env()?;
    if args.dev {
        cfg.dev_mode = true;
    }
    Ok(cfg)
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    tracing_subscriber::fmt()
        .with_env_filter(
            tracing_subscriber::EnvFilter::try_from_default_env()
                .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
        )
        .init();

    let cfg = load_config(&parse_args()?)?;
    let server = Server::builder().config(cfg).build().await?;
    server.serve().await?;
    Ok(())
}