Skip to main content

rill_runtime/handler/
mod.rs

1//! Handler adapters for the runtime engine.
2//!
3//! The runtime delegates capability execution to an [`InvokeHandler`](crate::server::InvokeHandler). Built-in
4//! handlers live in [`builtin`]; sandboxed WASM handlers live in [`wasm`] behind
5//! the `wasm` feature flag.
6
7pub mod builtin;
8#[cfg(feature = "wasm")]
9pub mod wasm;
10
11use serde::Serialize;
12
13/// Identity reported by the runtime in IPC v2 handshake responses.
14///
15/// `effective_capabilities` is the intersection of the model pack and handler
16/// pack capability lists. Only these capabilities can be invoked.
17#[derive(Debug, Clone, Serialize)]
18pub struct HandlerIdentity {
19    pub handler_id: String,
20    pub handler_version: String,
21    pub handler_api_version: u32,
22    pub effective_capabilities: Vec<String>,
23}
24
25/// Errors that arise while loading or preparing a handler for execution.
26#[derive(Debug, thiserror::Error)]
27pub enum HandlerLoadError {
28    #[error("handler pack error: {0}")]
29    Pack(#[from] crate::handler_package::HandlerPackError),
30    #[error("handler does not cover all model capabilities: missing {missing:?}")]
31    CapabilityMissing { missing: Vec<String> },
32    #[error("handler failed to initialize: {0}")]
33    Init(String),
34    #[error("guest metadata does not match signed manifest: {0}")]
35    MetadataMismatch(String),
36}
37
38/// Computes the effective capability set: the intersection of model and handler
39/// capabilities. Returns an error if the handler does not cover every model
40/// capability (first version rejects silent capability loss).
41pub fn effective_capabilities(
42    model: &[String],
43    handler: &[String],
44) -> Result<Vec<String>, HandlerLoadError> {
45    let mut model_sorted = model.to_vec();
46    model_sorted.sort();
47    let mut handler_sorted = handler.to_vec();
48    handler_sorted.sort();
49
50    let effective: Vec<String> = model_sorted
51        .iter()
52        .filter(|capability| handler_sorted.binary_search(capability).is_ok())
53        .cloned()
54        .collect();
55
56    if effective.len() != model.len() {
57        let missing: Vec<String> = model_sorted
58            .iter()
59            .filter(|capability| handler_sorted.binary_search(capability).is_err())
60            .cloned()
61            .collect();
62        return Err(HandlerLoadError::CapabilityMissing { missing });
63    }
64    Ok(effective)
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn effective_capabilities_intersect() {
73        let model = vec!["a".into(), "b".into()];
74        let handler = vec!["a".into(), "b".into(), "c".into()];
75        let result = effective_capabilities(&model, &handler).unwrap();
76        assert_eq!(result, vec!["a", "b"]);
77    }
78
79    #[test]
80    fn effective_capabilities_reject_missing() {
81        let model = vec!["a".into(), "b".into()];
82        let handler = vec!["a".into()];
83        let error = effective_capabilities(&model, &handler).unwrap_err();
84        assert!(
85            matches!(error, HandlerLoadError::CapabilityMissing { missing } if missing == vec!["b"])
86        );
87    }
88}