crepuscularity-cli 0.9.15

crepus CLI — scaffolding and builds for Crepuscularity (UNSTABLE; in active development).
//! Route parsed `clap` commands to module handlers.

use std::path::PathBuf;
use std::time::Instant;

use crate::cli::{
    BenchmarkCheckArgs, BenchmarkCommands, BenchmarkRunArgs, BrowserArg, Cli, Commands,
    IosCommands, MobileCommands, MobilePlatformArg, NativeCommands, NativeIosTargetArg,
    TuiCommands, WebCommands, WebextCommands,
};
use crate::target_build::ManifestBuildArgs;
use crate::ui;

pub fn run(cli: Cli) {
    let Some(command) = cli.command else {
        print_top_level_help();
        std::process::exit(0);
    };
    match command {
        Commands::New { name } => crate::new::run(&name),
        Commands::Init { kind, name } => run_init(&kind, &name),
        #[cfg(feature = "desktop")]
        Commands::Dev {
            bin,
            emit_events,
            build,
        } => {
            let options = build.into_options_or_exit();
            crate::dev::run(bin, options, emit_events);
        }
        Commands::Build {
            build,
            target_id,
            manifest,
            all,
            selector,
        } => run_top_level_build(build, target_id, manifest, all, selector),
        #[cfg(feature = "desktop")]
        Commands::Preview { file } => crate::preview::run_preview(file),
        Commands::Render {
            file,
            ctx,
            vars,
            component,
        } => crate::render::execute(file, ctx, vars, component),
        Commands::Web { command } => crate::web::execute(command),
        Commands::Webext { command } => crate::webext::execute(command),
        Commands::Ios { command } => crate::ios::execute(command),
        Commands::Tui { command } => crate::tui::execute(command),
        Commands::Native { command } => crate::native::execute(command),
        Commands::Mobile { command } => crate::mobile::execute(command),
        #[cfg(feature = "aurora")]
        Commands::Aurora { aurorality_args } => crate::aurora::run_forwarded(&aurorality_args),
        #[cfg(not(feature = "aurora"))]
        Commands::Aurora { .. } => {
            ui::error("Aurora not compiled in. Rebuild crepus with --features aurora.");
        }
        Commands::Embedded { command } => crate::embedded::execute(command),
        Commands::Benchmark { command, flat } => match command {
            Some(BenchmarkCommands::Check { args }) => {
                crate::benchmark::execute_check(benchmark_check_options(args));
            }
            Some(BenchmarkCommands::All { args }) | Some(BenchmarkCommands::Run { args }) => {
                crate::benchmark::execute_run(benchmark_run_options(args));
            }
            None => crate::benchmark::execute_run(benchmark_run_options(flat)),
        },
    }
}

fn run_init(kind: &str, name: &str) {
    match kind {
        "web" => crate::web::execute(WebCommands::New {
            name: name.to_string(),
        }),
        "webext" | "extension" | "browser-extension" => {
            crate::webext::execute(WebextCommands::New {
                name: name.to_string(),
            })
        }
        "tui" => crate::tui::execute(TuiCommands::New {
            name: name.to_string(),
        }),
        "native" => crate::native::execute(NativeCommands::New {
            name: name.to_string(),
        }),
        "mobile" => crate::mobile::execute(MobileCommands::New {
            name: name.to_string(),
        }),
        "ios" => crate::ios::execute(IosCommands::New {
            name: name.to_string(),
        }),
        #[cfg(feature = "desktop")]
        "gpui" | "app" | "desktop" => crate::new::run(name),
        #[cfg(not(feature = "desktop"))]
        "gpui" | "app" | "desktop" => {
            ui::error("GPUI scaffold not compiled in. Rebuild crepus with --features desktop.");
        }
        #[cfg(feature = "aurora")]
        "aurora" => crate::aurora::run_forwarded(&["new".to_string(), name.to_string()]),
        other => {
            let mut kinds = "web, webext, tui, native, ios, mobile".to_string();
            #[cfg(feature = "desktop")]
            {
                kinds.push_str(", gpui");
            }
            #[cfg(not(feature = "desktop"))]
            {
                kinds.push_str(", app");
            }
            #[cfg(feature = "aurora")]
            {
                kinds.push_str(", aurora");
            }
            ui::error(&format!("unknown init kind {other:?}; expected {kinds}"));
        }
    }
}

fn run_top_level_build(
    build: crate::build_options::BuildOptionsArgs,
    target_id: Option<String>,
    manifest: Option<PathBuf>,
    all: bool,
    selector: Option<String>,
) {
    let options = build.into_options_or_exit();
    let use_manifest = target_id.is_some()
        || manifest.is_some()
        || all
        || selector.is_some()
        || crate::target_build::has_manifest_targets(manifest.clone());
    if use_manifest {
        crate::target_build::execute(ManifestBuildArgs {
            options,
            target_id,
            selector,
            manifest,
            all,
        });
        return;
    }
    let t0 = Instant::now();
    let cwd = std::env::current_dir().unwrap_or_else(|e| {
        ui::error(&format!("cannot determine current directory: {e}"));
    });
    let sp = ui::spinner(if options.release() {
        "cargo build --release"
    } else {
        "cargo build"
    });
    #[cfg(feature = "desktop")]
    let outcome = crate::builder::cargo_build(&cwd, options, None);
    #[cfg(not(feature = "desktop"))]
    let outcome = crate::preview::cargo_build_fallback(&cwd, options);
    if outcome.success {
        ui::spinner_ok(&sp, "build succeeded");
        ui::done_in(t0.elapsed());
    } else {
        sp.finish_and_clear();
        ui::error("build failed");
    }
}

fn print_top_level_help() {
    use clap::CommandFactory;
    let _ = Cli::command().print_help();
}

pub fn browser_target(b: Option<BrowserArg>) -> Option<crepuscularity_webext::BrowserTarget> {
    b.map(|v| match v {
        BrowserArg::Chromium => crepuscularity_webext::BrowserTarget::Chromium,
        BrowserArg::Firefox => crepuscularity_webext::BrowserTarget::Firefox,
    })
}

pub fn benchmark_run_options(args: BenchmarkRunArgs) -> crate::benchmark::RunOptions {
    let verbose = args.verbose && !args.json && !args.quiet;
    let measure_memory = !args.no_memory;
    crate::benchmark::RunOptions {
        config_path: args.common.config,
        suite_filter: args.common.suite,
        only: args.common.only.map(|s| {
            s.split(',')
                .map(|x| x.trim().to_string())
                .filter(|x| !x.is_empty())
                .collect()
        }),
        json_out: args.json,
        dry_run: args.dry_run,
        clean: args.clean,
        repo_override: args.common.repo,
        work_override: args.work_root,
        verbose,
        measure_memory,
        #[cfg(feature = "tui")]
        no_tui: args.no_tui,
    }
}

pub fn benchmark_check_options(args: BenchmarkCheckArgs) -> crate::benchmark::CheckOptions {
    crate::benchmark::CheckOptions {
        config_path: args.common.config,
        suite_filter: args.common.suite,
        only: args.common.only.map(|s| {
            s.split(',')
                .map(|x| x.trim().to_string())
                .filter(|x| !x.is_empty())
                .collect()
        }),
        json_out: args.json,
        repo_override: args.common.repo,
    }
}

pub fn mobile_platform(p: MobilePlatformArg) -> crate::mobile::MobilePlatform {
    match p {
        MobilePlatformArg::Ios => crate::mobile::MobilePlatform::Ios,
        MobilePlatformArg::Android => crate::mobile::MobilePlatform::Android,
        MobilePlatformArg::All => crate::mobile::MobilePlatform::All,
    }
}

pub fn native_ios_target(t: NativeIosTargetArg) -> crate::native::IosBuildTarget {
    match t {
        NativeIosTargetArg::Simulator => crate::native::IosBuildTarget::Simulator,
        NativeIosTargetArg::Device => crate::native::IosBuildTarget::Device,
    }
}