znippy-cli 0.9.0

CLI for Znippy, a parallel chunked compression system.
//! Package-handler register + discovery.
//!
//! Rust has no runtime reflection / classpath scan, so the register is the
//! compiled-in catalog [`builtin_handlers`]. The tool autodiscovers it at
//! runtime via each handler's `meta()` — that is what `znippy handlers` lists
//! and what `--format <name>` selects from. Adding a native handler = one line
//! in [`builtin_handlers`].
//!
//! One znippy archive carries one package type, so `--format` selects exactly
//! one handler. Combining/merging or signing archives, and config-driven
//! orchestration of many handlers, are a higher layer — see the "Holger"
//! section in `design_plugins.md`.

use anyhow::{Result, anyhow};
use znippy_common::plugin::ArchiveTypePlugin;
use znippy_common::plugins::cargo_native::CargoPlugin;
use znippy_plugin_maven::NativeMavenPlugin;
use znippy_plugin_python::NativePythonPlugin;

/// The plugin register: every natively-compiled handler, sorted by `type_id`
/// (so cargo/rust = 1 lists first). To add a handler, add its constructor here.
pub fn builtin_handlers() -> Vec<Box<dyn ArchiveTypePlugin>> {
    let mut handlers: Vec<Box<dyn ArchiveTypePlugin>> = vec![
        Box::new(CargoPlugin::new()),
        Box::new(NativePythonPlugin),
        Box::new(NativeMavenPlugin),
    ];
    handlers.extend(znippy_common::plugins::skeletons::skeleton_handlers());
    handlers.sort_by_key(|h| h.type_id());
    handlers
}

/// Look up a handler by canonical name or alias (case-insensitive).
pub fn find_handler(query: &str) -> Result<Box<dyn ArchiveTypePlugin>> {
    let q = query.to_ascii_lowercase();
    builtin_handlers()
        .into_iter()
        .find(|h| {
            let m = h.meta();
            m.name.eq_ignore_ascii_case(&q) || m.aliases.iter().any(|a| a.eq_ignore_ascii_case(&q))
        })
        .ok_or_else(|| {
            anyhow!("unknown handler '{}'; run `znippy handlers` to list available handlers", query)
        })
}

/// Print the autodiscovered register (what `znippy handlers` shows).
pub fn print_catalog() {
    let handlers = builtin_handlers();
    println!("Package handlers ({}):\n", handlers.len());
    for h in &handlers {
        let m = h.meta();
        let aliases = if m.aliases.is_empty() {
            String::new()
        } else {
            format!(" (aliases: {})", m.aliases.join(", "))
        };
        println!("{}{}  [type_id {}]", m.name, aliases, m.type_id);
        if !m.ecosystem.is_empty() {
            println!("      ecosystem:  {}", m.ecosystem);
        }
        if !m.extensions.is_empty() {
            println!("      extensions: {}", m.extensions.join(" "));
        }
        if !m.description.is_empty() {
            println!("      {}", m.description);
        }
        for c in &m.commands {
            println!("      cmd `{} {}` — {}", m.name, c.name, c.about);
        }
    }
}