mecha10-cli 0.8.0

Mecha10 CLI tool
Documentation
//! Mecha10 CLI
//!
//! Command-line interface for managing Mecha10 robot projects.
//!
//! # Commands
//!
//! - `mecha10 init` - Initialize a new robot project
//! - `mecha10 dev` - Start development mode with hot-reload
//! - `mecha10 run` - Run the robot in production mode
//! - `mecha10 build` - Build the project
//! - `mecha10 train` - Submit a training job
//! - `mecha10 logs` - View logs from nodes, framework, and services (watch mode by default)
//! - `mecha10 sim` - Generate robot/environment simulation scenes
//! - `mecha10 target` - Show or update which platform(s) a target builds for

#![allow(
    clippy::print_stdout,
    clippy::print_stderr,
    reason = "this crate IS the CLI's terminal UI -- println!/eprintln! to the user's terminal is its intended output mechanism, not debug leftovers"
)]

mod commands;
mod component_catalog;
mod config_validation;
mod context;
mod dev;
mod handlers;
mod infrastructure;
mod init;
mod paths;
mod run;
mod services;
mod sim;
mod templates;
mod types;
mod ui;

use anyhow::Result;
use clap::Parser;
use context::CliContextBuilder;
use types::{Cli, Commands};

#[tokio::main]
async fn main() -> Result<()> {
    let _ = dotenvy::dotenv();

    let cli = Cli::parse();

    let log_level = match cli.log_level.as_str() {
        "debug" => tracing::Level::DEBUG,
        "info" => tracing::Level::INFO,
        "warn" => tracing::Level::WARN,
        "error" => tracing::Level::ERROR,
        _ => tracing::Level::INFO,
    };

    let _file_log_guard = if matches!(cli.command, Commands::Dev(_)) {
        let working_dir = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
        let logs_dir = working_dir.join(paths::project::LOGS_DIR);
        let _ = std::fs::create_dir_all(&logs_dir);
        let file_appender = tracing_appender::rolling::never(&logs_dir, "mecha10-dev.log");
        let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);

        use tracing_subscriber::prelude::*;
        tracing_subscriber::registry()
            .with(
                tracing_subscriber::fmt::layer()
                    .with_target(false)
                    .with_filter(tracing_subscriber::filter::LevelFilter::from_level(log_level)),
            )
            .with(
                tracing_subscriber::fmt::layer()
                    .with_target(false)
                    .with_ansi(false)
                    .with_writer(non_blocking)
                    .with_filter(tracing_subscriber::filter::LevelFilter::from_level(log_level)),
            )
            .init();

        Some(guard)
    } else {
        tracing_subscriber::fmt()
            .with_max_level(log_level)
            .with_target(false)
            .init();

        None
    };

    let mut ctx = CliContextBuilder::new()
        .config_path(cli.config.clone())
        .log_level(log_level)
        .build()?;

    match &cli.command {
        Commands::Add { command } => {
            handlers::add::handle_add(&mut ctx, command).await?;
        }

        Commands::Auth { command } => {
            use commands::auth::AuthCommand;
            match command {
                AuthCommand::Login(args) => {
                    handlers::auth::handle_login(args).await?;
                }
                AuthCommand::Logout => {
                    handlers::auth::handle_logout().await?;
                }
                AuthCommand::Whoami(args) => {
                    handlers::auth::handle_whoami(args).await?;
                }
            }
        }

        Commands::Build { command } => {
            handlers::build::handle_build(&mut ctx, command).await?;
        }

        Commands::Cache { command } => {
            use commands::cache::CacheCommand;
            match command {
                CacheCommand::Clean => {
                    handlers::cache::handle_cache_clean().await?;
                }
            }
        }

        Commands::Config { command } => {
            use commands::config::ConfigCommand;
            match command {
                ConfigCommand::Push(args) => {
                    handlers::config::handle_config_push(&mut ctx, args).await?;
                }
                ConfigCommand::Pull(args) => {
                    handlers::config::handle_config_pull(&mut ctx, args).await?;
                }
                ConfigCommand::List(args) => {
                    handlers::config::handle_config_list(&mut ctx, args).await?;
                }
                ConfigCommand::Show => {
                    handlers::config::handle_config_show(&mut ctx).await?;
                }
                ConfigCommand::Validate(args) => {
                    handlers::config::handle_config_validate(&mut ctx, args).await?;
                }
                ConfigCommand::Edit => {
                    handlers::config::handle_config_edit(&mut ctx).await?;
                }
                ConfigCommand::Sync => {
                    handlers::config::handle_config_sync(&mut ctx).await?;
                }
            }
        }

        Commands::Dev(dev_args) => {
            handlers::dev::handle_dev(&mut ctx, dev_args).await?;
        }

        Commands::Diagnostics(args) => {
            handlers::diagnostics::handle_diagnostics(&mut ctx, args).await?;
        }

        Commands::Format(format_args) => {
            use handlers::format::FormatArgs;
            let args = FormatArgs {
                check: format_args.check,
            };
            handlers::format::handle_format(&mut ctx, &args).await?;
        }

        Commands::Init(init_args) => {
            handlers::init::handle_init(&mut ctx, init_args).await?;
        }

        Commands::Lint(lint_args) => {
            use handlers::lint::LintArgs;
            let args = LintArgs { fix: lint_args.fix };
            handlers::lint::handle_lint(&mut ctx, &args).await?;
        }

        Commands::Logs { command } => {
            handlers::logs::handle_logs_command(&mut ctx, command).await?;
        }

        Commands::Model { command } => {
            handlers::model::handle_model(&mut ctx, command).await?;
        }

        Commands::Ps { command } => {
            handlers::ps::handle_ps(&mut ctx, command).await?;
        }

        Commands::Robot { command } => {
            handlers::robot::handle_robot(&mut ctx, command).await?;
        }

        Commands::Run { command } => {
            handlers::run::handle_run(&mut ctx, command).await?;
        }

        Commands::Setup(setup_args) => {
            use handlers::setup::SetupArgs;
            let args = SetupArgs {
                check_only: setup_args.check_only,
                install_deps: !setup_args.non_interactive,
                quiet: false,
            };
            handlers::setup::handle_setup(&mut ctx, &args).await?;
        }

        Commands::Sim { command } => {
            handlers::sim::handle_sim(&mut ctx, command).await?;
        }

        Commands::Stop { command } => {
            handlers::stop::handle_stop(&mut ctx, command).await?;
        }

        Commands::Target { command } => {
            handlers::target::handle_target(&mut ctx, command.as_ref()).await?;
        }

        Commands::Topics { command } => {
            use commands::TopicsCommand;
            use handlers::topics::TopicsListArgs;

            match command {
                TopicsCommand::List {
                    pattern,
                    verbose,
                    grouped,
                } => {
                    let args = TopicsListArgs {
                        pattern: pattern.clone(),
                        verbose: *verbose,
                        grouped: *grouped,
                    };
                    handlers::topics::handle_topics_list(&mut ctx, &args).await?;
                }
            }
        }

        Commands::Topology(topology_args) => {
            handlers::topology::handle_topology(&mut ctx, topology_args).await?;
        }

        Commands::Train(train_args) => {
            handlers::train::handle_train(&mut ctx, train_args).await?;
        }

        Commands::Upload(upload_args) => {
            handlers::upload::handle_upload(&mut ctx, upload_args).await?;
        }

        Commands::Validate(args) => {
            handlers::config::handle_config_validate(&mut ctx, args).await?;
        }
    }

    Ok(())
}