Skip to main content

znippy_cli/
handlers.rs

1//! Package-handler register + discovery.
2//!
3//! Rust has no runtime reflection / classpath scan, so the register is the
4//! compiled-in catalog [`builtin_handlers`]. The tool autodiscovers it at
5//! runtime via each handler's `meta()` — that is what `znippy handlers` lists
6//! and what `--format <name>` selects from. Adding a native handler = one line
7//! in [`builtin_handlers`].
8//!
9//! One znippy archive carries one package type, so `--format` selects exactly
10//! one handler. Combining/merging or signing archives, and config-driven
11//! orchestration of many handlers, are a higher layer — that layer is the
12//! sibling `holger` repo, not znippy.
13//!
14//! Where a handler's *source* lives — in `znippy-common/src/plugins/` or in its
15//! own `znippy-plugin-*` crate — is decided by rule **P-5** in
16//! `.nornir/plugins-design.md` §f3. Both tiers register right here and are
17//! indistinguishable to `znippy handlers`.
18
19use anyhow::{Result, anyhow};
20use znippy_common::plugin::ArchiveTypePlugin;
21use znippy_common::plugins::cargo_native::CargoPlugin;
22use znippy_common::plugins::conda_native::CondaPlugin;
23use znippy_common::plugins::deb_native::DebPlugin;
24use znippy_common::plugins::gem_native::GemPlugin;
25use znippy_common::plugins::npm_native::NpmPlugin;
26use znippy_common::plugins::rpm_native::RpmPlugin;
27use znippy_plugin_git::NativeGitPlugin;
28use znippy_plugin_maven::NativeMavenPlugin;
29use znippy_plugin_media::NativeMediaPlugin;
30use znippy_plugin_python::NativePythonPlugin;
31use znippy_plugin_rust_toolchain::NativeRustToolchainPlugin;
32use znippy_plugin_skidbladnir::NativeSkidbladnirPlugin;
33
34/// The plugin register: every natively-compiled handler, sorted by `type_id`
35/// (so cargo/rust = 1 lists first). To add a handler, add its constructor here.
36pub fn builtin_handlers() -> Vec<Box<dyn ArchiveTypePlugin>> {
37    let mut handlers: Vec<Box<dyn ArchiveTypePlugin>> = vec![
38        Box::new(CargoPlugin::new()),
39        Box::new(NativePythonPlugin),
40        Box::new(NativeMavenPlugin),
41        Box::new(NpmPlugin),
42        Box::new(GemPlugin),
43        Box::new(CondaPlugin),
44        Box::new(RpmPlugin),
45        Box::new(DebPlugin),
46        Box::new(NativeMediaPlugin::new()),
47        Box::new(NativeSkidbladnirPlugin::new()),
48        Box::new(NativeRustToolchainPlugin::new()),
49        Box::new(NativeGitPlugin::new()),
50    ];
51    handlers.extend(znippy_common::plugins::skeletons::skeleton_handlers());
52    handlers.sort_by_key(|h| h.type_id());
53    handlers
54}
55
56/// Look up a handler by canonical name or alias (case-insensitive).
57pub fn find_handler(query: &str) -> Result<Box<dyn ArchiveTypePlugin>> {
58    let q = query.to_ascii_lowercase();
59    builtin_handlers()
60        .into_iter()
61        .find(|h| {
62            let m = h.meta();
63            m.name.eq_ignore_ascii_case(&q) || m.aliases.iter().any(|a| a.eq_ignore_ascii_case(&q))
64        })
65        .ok_or_else(|| {
66            anyhow!("unknown handler '{}'; run `znippy handlers` to list available handlers", query)
67        })
68}
69
70/// Print the autodiscovered register (what `znippy handlers` shows).
71pub fn print_catalog() {
72    let handlers = builtin_handlers();
73    println!("Package handlers ({}):\n", handlers.len());
74    for h in &handlers {
75        let m = h.meta();
76        let aliases = if m.aliases.is_empty() {
77            String::new()
78        } else {
79            format!(" (aliases: {})", m.aliases.join(", "))
80        };
81        println!("  • {}{}  [type_id {}]", m.name, aliases, m.type_id);
82        if !m.ecosystem.is_empty() {
83            println!("      ecosystem:  {}", m.ecosystem);
84        }
85        if !m.extensions.is_empty() {
86            println!("      extensions: {}", m.extensions.join(" "));
87        }
88        if !m.description.is_empty() {
89            println!("      {}", m.description);
90        }
91        for c in &m.commands {
92            println!("      cmd `{} {}` — {}", m.name, c.name, c.about);
93        }
94    }
95}