use crate::metadata::NodeMetadata;
use crate::processor::NodeProcessor;
use std::collections::HashMap;
pub struct NodeRegistry {
processors: HashMap<String, Box<dyn NodeProcessor>>,
}
impl NodeRegistry {
pub fn new() -> Self {
Self {
processors: HashMap::new(),
}
}
pub fn register(&mut self, key: &str, processor: Box<dyn NodeProcessor>) {
self.processors.insert(key.to_string(), processor);
}
pub fn resolve(
&self,
node_type: &str,
_params: &serde_json::Map<String, serde_json::Value>,
) -> Option<&dyn NodeProcessor> {
self.processors.get(node_type).map(|b| b.as_ref())
}
pub fn len(&self) -> usize {
self.processors.len()
}
pub fn is_empty(&self) -> bool {
self.processors.is_empty()
}
pub fn catalog(&self) -> Vec<NodeMetadata> {
self.processors.values().map(|p| p.metadata()).collect()
}
}
impl Default for NodeRegistry {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::context::ProcessContext;
use crate::errors::BntoError;
use crate::processor::{NodeInput, NodeOutput, OutputFile};
use crate::progress::ProgressReporter;
struct MockProcessor {
mock_name: String,
}
impl MockProcessor {
fn new(name: &str) -> Self {
Self {
mock_name: name.to_string(),
}
}
}
impl NodeProcessor for MockProcessor {
fn name(&self) -> &str {
&self.mock_name
}
fn process(
&self,
input: NodeInput,
_progress: &ProgressReporter,
_ctx: &dyn ProcessContext,
) -> Result<NodeOutput, BntoError> {
Ok(NodeOutput {
files: vec![OutputFile {
data: input.data,
filename: input.filename,
mime_type: input
.mime_type
.unwrap_or_else(|| "application/octet-stream".to_string()),
}],
metadata: serde_json::Map::new(),
})
}
}
#[test]
fn test_new_registry_is_empty() {
let registry = NodeRegistry::new();
assert!(registry.is_empty());
assert_eq!(registry.len(), 0);
}
#[test]
fn test_register_and_resolve() {
let mut registry = NodeRegistry::new();
registry.register("image-compress", Box::new(MockProcessor::new("compress")));
let params = serde_json::Map::new();
let processor = registry.resolve("image-compress", ¶ms);
assert!(processor.is_some());
assert_eq!(processor.unwrap().name(), "compress");
}
#[test]
fn test_resolve_unknown_type_returns_none() {
let registry = NodeRegistry::new();
let params = serde_json::Map::new();
let processor = registry.resolve("unknown-type", ¶ms);
assert!(processor.is_none());
}
#[test]
fn test_register_multiple_processors() {
let mut registry = NodeRegistry::new();
registry.register("image-compress", Box::new(MockProcessor::new("compress")));
registry.register("image-resize", Box::new(MockProcessor::new("resize")));
registry.register(
"spreadsheet-clean",
Box::new(MockProcessor::new("clean-csv")),
);
assert_eq!(registry.len(), 3);
let params = serde_json::Map::new();
assert_eq!(
registry.resolve("image-compress", ¶ms).unwrap().name(),
"compress"
);
assert_eq!(
registry.resolve("image-resize", ¶ms).unwrap().name(),
"resize"
);
assert_eq!(
registry
.resolve("spreadsheet-clean", ¶ms)
.unwrap()
.name(),
"clean-csv"
);
}
#[test]
fn test_register_overwrites_existing() {
let mut registry = NodeRegistry::new();
registry.register("image-compress", Box::new(MockProcessor::new("old")));
registry.register("image-compress", Box::new(MockProcessor::new("new")));
let params = serde_json::Map::new();
assert_eq!(
registry.resolve("image-compress", ¶ms).unwrap().name(),
"new"
);
}
}