dae 0.1.13

A Linux daemon management TUI and CLI with a subdued scrin interface.
mod app;
mod daemon;
mod ui;

use anyhow::Result;
use app::{App, Cli, CliCommand};
use clap::Parser;

fn main() -> Result<()> {
    let cli = Cli::parse();
    if let Some(command) = cli.command.clone() {
        run_cli_command(&cli, command)
    } else {
        App::new(cli).run()
    }
}

fn run_cli_command(cli: &Cli, command: CliCommand) -> Result<()> {
    match command {
        CliCommand::List => print_daemon_list(cli),
        CliCommand::Status { unit } => print_status(&unit),
        CliCommand::Journal { unit } => print_journal(cli, &unit),
        CliCommand::Events { unit } => print_events(cli, &unit),
        CliCommand::Start { unit } => run_action(&unit, daemon::DaemonAction::Start),
        CliCommand::Stop { unit } => run_action(&unit, daemon::DaemonAction::Stop),
        CliCommand::Restart { unit } => run_action(&unit, daemon::DaemonAction::Restart),
        CliCommand::Reload { unit } => run_action(&unit, daemon::DaemonAction::Reload),
        CliCommand::Enable { unit } => run_action(&unit, daemon::DaemonAction::Enable),
        CliCommand::Disable { unit } => run_action(&unit, daemon::DaemonAction::Disable),
        CliCommand::Term { unit } => run_action(&unit, daemon::DaemonAction::KillTerm),
        CliCommand::Kill { unit } => run_action(&unit, daemon::DaemonAction::KillKill),
    }
}

fn print_daemon_list(cli: &Cli) -> Result<()> {
    let mut app = App::new(cli.clone());
    app.daemons = daemon::load_daemons()?;
    let rows = app.visible_indices();

    println!(
        "{:<44} {:<18} {:>8} {:>10} SIGNAL",
        "UNIT", "STATE", "PID", "MEM"
    );
    for idx in rows {
        let daemon = &app.daemons[idx];
        println!(
            "{:<44} {:<18} {:>8} {:>10} {}",
            daemon.unit,
            daemon.state_label(),
            daemon
                .main_pid
                .map(|pid| pid.to_string())
                .unwrap_or_else(|| "-".to_string()),
            daemon::format_bytes(daemon.memory_bytes),
            daemon.anomaly_summary()
        );
    }
    Ok(())
}

fn print_status(unit: &str) -> Result<()> {
    let unit = daemon::service_unit(unit);
    let report = daemon::status_report(&unit)?;
    print_report(&report)
}

fn print_journal(cli: &Cli, unit: &str) -> Result<()> {
    let unit = daemon::service_unit(unit);
    for line in daemon::load_journal(&unit, cli.lookback_hours, cli.journal_lines)? {
        println!("{line}");
    }
    Ok(())
}

fn print_events(cli: &Cli, unit: &str) -> Result<()> {
    let unit = daemon::service_unit(unit);
    let journal = daemon::load_journal(&unit, cli.lookback_hours, cli.journal_lines)?;
    for line in daemon::notable_events(&journal) {
        println!("{line}");
    }
    Ok(())
}

fn run_action(unit: &str, action: daemon::DaemonAction) -> Result<()> {
    let unit = daemon::service_unit(unit);
    let report = daemon::apply_action(&unit, action)?;
    print_report(&report)
}

fn print_report(report: &daemon::CommandReport) -> Result<()> {
    if !report.output.is_empty() {
        println!("{}", report.output);
    }
    if report.ok {
        Ok(())
    } else {
        anyhow::bail!("{} failed", report.command)
    }
}