nooise 1.0.3

Ambient music generator for the terminal
use std::env;
use std::error::Error;
use std::process::Command;

mod audio;
mod fluid;
mod fx;
mod synth;

fn main() -> Result<(), Box<dyn Error>> {
    match parse_args(env::args().skip(1))? {
        CliCommand::Run => fluid::run(),
        CliCommand::Update => update_nooise(),
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CliCommand {
    Run,
    Update,
}

fn parse_args<I>(mut args: I) -> Result<CliCommand, Box<dyn Error>>
where
    I: Iterator<Item = String>,
{
    let Some(arg) = args.next() else {
        return Ok(CliCommand::Run);
    };

    if args.next().is_some() {
        return Err(format!("unexpected argument after {arg}").into());
    }

    match arg.as_str() {
        "-h" | "--help" => {
            print_usage();
            std::process::exit(0);
        }
        "update" | "upgrade" => Ok(CliCommand::Update),
        other => Err(format!("unknown argument: {other}").into()),
    }
}

fn print_usage() {
    println!("Usage: nooise [update|upgrade]");
}

fn update_nooise() -> Result<(), Box<dyn Error>> {
    println!("Updating nooise from crates.io...");
    let status = Command::new("cargo")
        .args(["install", "nooise", "--locked", "--force"])
        .status()
        .map_err(|e| format!("failed to run cargo install nooise: {e}"))?;

    if status.success() {
        Ok(())
    } else {
        Err(format!("cargo install nooise failed with {status}").into())
    }
}

#[cfg(test)]
mod tests {
    use super::{CliCommand, parse_args};

    fn args(items: &[&str]) -> impl Iterator<Item = String> {
        items
            .iter()
            .map(|item| item.to_string())
            .collect::<Vec<_>>()
            .into_iter()
    }

    #[test]
    fn no_args_runs_app() {
        assert_eq!(parse_args(args(&[])).unwrap(), CliCommand::Run);
    }

    #[test]
    fn update_and_upgrade_run_updater() {
        assert_eq!(parse_args(args(&["update"])).unwrap(), CliCommand::Update);
        assert_eq!(parse_args(args(&["upgrade"])).unwrap(), CliCommand::Update);
    }

    #[test]
    fn unknown_arg_errors() {
        assert!(parse_args(args(&["--experiment"])).is_err());
    }
}