shine-cli 1.1.1

Cross-platform CLI for managed shell commands, app configs, and machine setup
Documentation
use anyhow::{Context, Result};
use console::{Style, style};
use dialoguer::theme::ColorfulTheme;

use crate::colors;
use crate::sys::execution::{format_command_preview, status_symbol, status_text, sys_init_command};
use crate::sys::run_manifest::SysRunEntry;
use crate::sys::{LoadedSysPreset, ResolvedSelection, SysDriverKind, SysItem, SysItemMode};

pub(super) fn sys_init_theme() -> ColorfulTheme {
    ColorfulTheme {
        prompt_prefix: style(">".to_string()).for_stderr().cyan().bold(),
        prompt_suffix: style("".to_string()).for_stderr(),
        success_prefix: style("".to_string()).for_stderr().green(),
        success_suffix: style("".to_string()).for_stderr(),
        active_item_prefix: style("".to_string()).for_stderr().cyan().bold(),
        inactive_item_prefix: style(" ".to_string()).for_stderr(),
        checked_item_prefix: style("[x]".to_string()).for_stderr().green(),
        unchecked_item_prefix: style("[ ]".to_string()).for_stderr().black().bright(),
        prompt_style: Style::new().for_stderr().bold(),
        active_item_style: Style::new().for_stderr().cyan(),
        inactive_item_style: Style::new().for_stderr(),
        values_style: Style::new().for_stderr().cyan(),
        hint_style: Style::new().for_stderr().black().bright(),
        ..ColorfulTheme::default()
    }
}

pub(in crate::sys) fn print_available_item(item: &SysItem, entry: Option<&SysRunEntry>) {
    let kind = item_mode_name(item.mode);
    let status = entry
        .map(|entry| {
            let symbol = status_symbol(entry.status);
            format!(
                "{} {}",
                colors::symbol(symbol),
                colors::status_label(status_text(entry.status), symbol)
            )
        })
        .unwrap_or_else(|| colors::dim("not recorded"));
    println!(
        "    {:<18} {:<9} {}  {}",
        item.id,
        format!("[{kind}]"),
        colors::bold(&item.label),
        status
    );
    if !item.description.is_empty() {
        println!("      {}", colors::dim(&item.description));
    }
    if item.mode == SysItemMode::Managed {
        println!(
            "      {}",
            colors::dim(&format!("Run: shine sys apply {}", item.id))
        );
    }
}

pub(in crate::sys) fn item_mode_name(mode: SysItemMode) -> &'static str {
    match mode {
        SysItemMode::Init => "init",
        SysItemMode::Managed => "managed",
    }
}

pub(in crate::sys) fn driver_name(driver: SysDriverKind) -> &'static str {
    match driver {
        SysDriverKind::Script => "script",
        SysDriverKind::SplitDns => "split-dns",
        SysDriverKind::ManagedFile => "managed-file",
    }
}

pub(in crate::sys) async fn print_dry_run(
    os_id: &str,
    loaded: &LoadedSysPreset,
    selection: &ResolvedSelection,
    sys_shell: &str,
    proxy_env: &[(&'static str, String)],
) -> Result<()> {
    println!("{}", colors::dim("[dry-run] System init preview"));
    println!("  OS: {os_id}");
    println!("  Shell: {sys_shell}");
    println!("  Selection: {}", selection.source.describe());
    if !proxy_env.is_empty() {
        println!("  Proxy env:");
        for (key, value) in proxy_env {
            println!("    {}", colors::dim(&format!("{key}={value}")));
        }
    }
    println!(
        "  Items: {}",
        if selection.item_ids.is_empty() {
            "(none)".to_string()
        } else {
            selection.item_ids.join(", ")
        }
    );
    println!("  Script: {}", loaded.script_path.display());
    let command = sys_init_command(os_id);
    println!("  Commands:");
    for item_id in &selection.item_ids {
        println!(
            "    {}",
            format_command_preview(&command, &loaded.script_path, std::slice::from_ref(item_id))
        );
    }
    if !selection.item_ids.is_empty() {
        println!("    shine internal sys profile pre/post update");
    }
    println!();
    let content = tokio::fs::read_to_string(&loaded.script_path)
        .await
        .with_context(|| format!("reading {}", loaded.script_path.display()))?;
    println!("{}", colors::dim("--- script content ---"));
    print!("{content}");
    Ok(())
}