Skip to main content

rskit_inference/
registry.rs

1use std::{collections::BTreeMap, sync::Arc};
2
3use thiserror::Error;
4
5use crate::{Inference, InferenceError};
6
7/// Factory function used to build a configured inference adapter.
8pub type Factory = Arc<dyn Fn() -> Result<Arc<dyn Inference>, InferenceError> + Send + Sync>;
9
10/// Explicit registry of inference adapter factories.
11#[derive(Default)]
12pub struct Registry {
13    factories: BTreeMap<String, Factory>,
14}
15
16impl Registry {
17    /// Create an empty registry.
18    #[must_use]
19    pub fn new() -> Self {
20        Self::default()
21    }
22
23    /// Register an adapter factory under a stable kind.
24    pub fn register(&mut self, kind: &str, factory: Factory) -> Result<(), RegistryError> {
25        let kind = Self::normalize_kind(kind).map_err(|_| RegistryError::EmptyKind)?;
26        if self.factories.contains_key(&kind) {
27            return Err(RegistryError::DuplicateKind(kind));
28        }
29        self.factories.insert(kind, factory);
30        Ok(())
31    }
32
33    /// Build the configured adapter registered for `kind`.
34    pub fn build(&self, kind: &str) -> Result<Arc<dyn Inference>, InferenceError> {
35        let kind = Self::normalize_kind(kind)?;
36        let factory = self
37            .factories
38            .get(&kind)
39            .ok_or_else(|| InferenceError::Decode(format!("unknown inference adapter {kind:?}")))?;
40        factory()
41    }
42
43    fn normalize_kind(kind: &str) -> Result<String, InferenceError> {
44        let kind = kind.trim();
45        if kind.is_empty() {
46            return Err(InferenceError::InvalidInput(
47                "inference adapter kind is required".to_owned(),
48            ));
49        }
50        Ok(kind.to_owned())
51    }
52
53    /// Return registered kinds in stable order.
54    #[must_use]
55    pub fn kinds(&self) -> Vec<String> {
56        self.factories.keys().cloned().collect()
57    }
58}
59
60/// Registry mutation failure.
61#[derive(Debug, Error, PartialEq, Eq)]
62#[non_exhaustive]
63pub enum RegistryError {
64    /// Adapter kind was empty or whitespace.
65    #[error("inference adapter kind is required")]
66    EmptyKind,
67    /// Adapter kind is already registered.
68    #[error("inference adapter {0:?} already registered")]
69    DuplicateKind(String),
70}
71
72/// Create an empty registry.
73///
74/// Backends are intentionally not auto-registered.
75/// Consumers opt in by calling adapter crate `register(&mut Registry, Config)` functions during composition.
76#[must_use]
77pub fn default_registry() -> Registry {
78    Registry::new()
79}
80
81#[cfg(test)]
82mod tests {
83    use rskit_errors::ErrorCode;
84
85    use super::{Registry, RegistryError};
86    use crate::InferenceError;
87
88    #[test]
89    fn register_rejects_empty_kind() {
90        let mut registry = Registry::new();
91        let factory = std::sync::Arc::new(|| unreachable!("factory should not run"));
92        let err = registry.register(" \t ", factory).unwrap_err();
93        assert_eq!(err, RegistryError::EmptyKind);
94    }
95
96    #[test]
97    fn build_rejects_empty_kind_as_invalid_input() {
98        let Err(err) = Registry::new().build(" \t ") else {
99            panic!("empty inference adapter kind should be rejected");
100        };
101        assert!(matches!(err, InferenceError::InvalidInput(_)));
102
103        let app_error = rskit_errors::AppError::from(err);
104        assert_eq!(app_error.code(), ErrorCode::InvalidInput);
105    }
106}