zinit 0.3.9

Process supervisor with dependency management
Documentation
//! zinit CLI - Command line interface for the zinit process supervisor.

use std::path::Path;
use std::process::ExitCode;

use clap::Parser;
use zinit::{
    ZinitHandle,
    client::cli::{Cli, Commands, XinetCommands, commands},
};

#[cfg(feature = "tui")]
use zinit::client::tui;

fn main() -> ExitCode {
    // Check if first argument is a Rhai script or directory (for shebang support)
    // This allows: #!/usr/bin/env zinit
    //              or: zinit script.rhai
    //              or: zinit ./scripts/
    #[cfg(feature = "rhai")]
    {
        let args: Vec<String> = std::env::args().collect();
        if args.len() >= 2 {
            let first_arg = &args[1];
            // Skip if it looks like a flag or known subcommand
            if !first_arg.starts_with('-') {
                let path = Path::new(first_arg);
                let is_rhai_script = first_arg.ends_with(".rhai") && path.is_file();
                let is_rhai_dir = path.is_dir()
                    && std::fs::read_dir(path)
                        .map(|entries| {
                            entries.filter_map(|e| e.ok()).any(|e| {
                                e.path()
                                    .extension()
                                    .map(|ext| ext == "rhai")
                                    .unwrap_or(false)
                            })
                        })
                        .unwrap_or(false);

                if is_rhai_script || is_rhai_dir {
                    return match commands::cmd_rhai(&path.to_path_buf()) {
                        Ok(()) => ExitCode::SUCCESS,
                        Err(e) => {
                            eprintln!("Error: {}", e);
                            ExitCode::FAILURE
                        }
                    };
                }
            }
        }
    }

    let cli = Cli::parse();

    // Handle TUI command (doesn't use standard client flow)
    #[cfg(feature = "tui")]
    if let Commands::Tui = &cli.command {
        let socket_path = cli.socket.clone().unwrap_or_else(|| {
            let socket_str = std::env::var("ZINIT_SOCKET")
                .unwrap_or_else(|_| format!("{}/.zinit/socket", std::env::var("HOME").unwrap()));
            std::path::PathBuf::from(socket_str)
        });
        return match tui::run(&socket_path) {
            Ok(()) => ExitCode::SUCCESS,
            Err(e) => {
                eprintln!("Error: {}", e);
                ExitCode::FAILURE
            }
        };
    }

    // Create herolib client
    let client = match ZinitHandle::new() {
        Ok(c) => c,
        Err(e) => {
            eprintln!("Error: Failed to connect to zinit daemon: {}", e);
            eprintln!("Is zinit-server running?");
            return ExitCode::FAILURE;
        }
    };

    let result = match cli.command {
        Commands::List => commands::cmd_list(&client),
        Commands::Status { name } => commands::cmd_status(&client, &name),
        Commands::Start { name, tree } => commands::cmd_start(&client, &name, tree),
        Commands::Stop { name } => commands::cmd_stop(&client, &name),
        Commands::Restart { name } => commands::cmd_restart(&client, &name),
        Commands::Kill { name, signal } => commands::cmd_kill(&client, &name, signal.as_deref()),
        Commands::Why { name } => commands::cmd_why(&client, &name),
        Commands::Tree => commands::cmd_tree(&client),
        Commands::Remove { name } => commands::cmd_remove(&client, &name),
        Commands::Reload => Err("reload command not supported in this version".to_string()),
        Commands::Logs {
            name,
            lines,
            follow,
        } => commands::cmd_logs(&client, &name, lines, follow),
        Commands::Ping => commands::cmd_ping(&client),
        Commands::Shutdown => commands::cmd_shutdown(&client),
        Commands::AddService {
            file,
            name,
            exec,
            dir,
            oneshot,
            envs,
            after,
            requires,
            wants,
            conflicts,
            restart,
            restart_delay,
            restart_delay_max,
            max_restarts,
            persist,
            ephemeral: _,
        } => commands::cmd_add_service(
            &client,
            file,
            name,
            exec,
            dir,
            oneshot,
            envs,
            after,
            requires,
            wants,
            conflicts,
            restart,
            restart_delay,
            restart_delay_max,
            max_restarts,
            persist,
        ),
        Commands::DebugState => commands::cmd_debug_state(&client),
        Commands::DebugProcs { name } => commands::cmd_debug_procs(&client, &name),
        Commands::Xinet { command } => match command {
            XinetCommands::Register {
                name,
                listen,
                backend,
                service,
                connect_timeout,
                idle_timeout,
                single,
            } => commands::cmd_xinet_register(
                &client,
                name,
                listen,
                backend,
                service,
                connect_timeout,
                idle_timeout,
                single,
            ),
            XinetCommands::Unregister { name } => commands::cmd_xinet_unregister(&client, &name),
            XinetCommands::List => commands::cmd_xinet_list(&client),
            XinetCommands::Status { name } => commands::cmd_xinet_status(&client, name.as_deref()),
        },
        Commands::Poweroff => commands::cmd_poweroff(&client),
        Commands::Reboot => commands::cmd_reboot(&client),
        #[cfg(feature = "tui")]
        Commands::Tui => unreachable!(),
        #[cfg(feature = "rhai")]
        Commands::Rhai { path } => commands::cmd_rhai(&path),
    };

    match result {
        Ok(()) => ExitCode::SUCCESS,
        Err(e) => {
            eprintln!("Error: {}", e);
            ExitCode::FAILURE
        }
    }
}