server/lib/src/handlers.rs
//! **`ron_handler` — which handler reads a repository's container.**
//!
//! `ron_repo_type` and `ron_upstream_format` name the *container*. `znippy` is a
//! container: compressed files in a directory, sometimes with an extra index and
//! extra functionality. A Skidbladnir deploy bundle, a Rust-toolchain dev bundle
//! and a plain sealed `.znippy` are all `ArtifactFormat::Znippy` and all
//! correct — what the config could not say is **who reads the extra index**.
//!
//! ```text
//! ron_upstream_format = "znippy" the container
//! ron_handler = "skidbladnir" the reader; empty = generic znippy
//! ```
//!
//! Adding an `ArtifactFormat` variant per handler was the other way to do this,
//! and it is the wrong level: it would describe the payload as if it were a
//! format, and touch `ArtifactFormat`, `ArtifactFormat::home`,
//! `znippy_type_id` and an exhaustive `match` in sixteen backend crates. This
//! costs one optional string.
//!
//! # What is in the catalog
//!
//! * the handlers **linked** into this holger, registered here;
//! * plus every `.wasm` in [`Holger::ron_plugin_dir`](crate::Holger), when the
//! `wasm-plugins` feature is on.
//!
//! Both kinds register under the name their own manifest declares, so one config
//! string resolves to either without the config knowing which — which is the
//! whole point. A new handler is then a string plus a file, with no holger
//! rebuild.
pub use holger_plugin_host::{HandlerCatalog, HandlerError, Origin, RejectedModule};
use holger_handler_rust_toolchain::RustToolchainHandler;
use holger_handler_skidbladnir::SkidbladnirHandler;
/// The handlers compiled into this holger.
///
/// Each registers under **its own** `manifest().handler`, never a name chosen
/// here: a registrar that passed the name in could mount one handler under
/// another's string, and `ron_handler` would then mean different things
/// depending on whether it resolved to a linked handler or to a module.
///
/// A collision between two builtins is an `Err`, not a silent replacement —
/// though with the names being handler-crate constants it is a build-time
/// impossibility today, which is exactly why it must not be an `unwrap`: the
/// third handler is the one that would hit it.
pub fn builtin_catalog() -> anyhow::Result<HandlerCatalog> {
let mut catalog = HandlerCatalog::new();
catalog
.register_linked(SkidbladnirHandler::new)
.map_err(|e| anyhow::anyhow!("registering the linked Skidbladnir handler: {e}"))?;
catalog
.register_linked(RustToolchainHandler::new)
.map_err(|e| anyhow::anyhow!("registering the linked rust-toolchain handler: {e}"))?;
Ok(catalog)
}
#[cfg(test)]
mod tests {
use super::*;
use holger_plugin_host::Origin;
/// The names an operator writes in a config, asserted against the catalog
/// rather than against the crates' constants — a test that read
/// `HANDLER_NAME` back would pass even if registration used something else.
#[test]
fn the_builtins_are_the_two_bundle_handlers() {
let c = builtin_catalog().expect("the builtin catalog must build");
assert_eq!(
c.accepted(),
vec!["rust-toolchain".to_string(), "skidbladnir".to_string()]
);
assert_eq!(c.origin("skidbladnir"), Some(Origin::Linked));
assert_eq!(c.origin("rust-toolchain"), Some(Origin::Linked));
assert_eq!(c.origin("skidbladnr"), None, "a typo resolved to a handler");
}
}