1use std::collections::HashMap;
2use crate::plugin::{DataSource, TransformMiddleware, ChartRenderer, DatasourceResolver};
3
4pub struct ChartMLRegistry {
6 data_sources: HashMap<String, Box<dyn DataSource>>,
7 transforms: Vec<Box<dyn TransformMiddleware>>,
8 renderers: HashMap<String, Box<dyn ChartRenderer>>,
9 datasource_resolver: Option<Box<dyn DatasourceResolver>>,
10}
11
12impl ChartMLRegistry {
13 pub fn new() -> Self {
14 Self {
15 data_sources: HashMap::new(),
16 transforms: Vec::new(),
17 renderers: HashMap::new(),
18 datasource_resolver: None,
19 }
20 }
21
22 pub fn register_data_source(&mut self, name: &str, source: impl DataSource + 'static) {
23 self.data_sources.insert(name.to_string(), Box::new(source));
24 }
25
26 pub fn register_transform(&mut self, middleware: impl TransformMiddleware + 'static) {
27 self.transforms.push(Box::new(middleware));
28 }
29
30 pub fn set_transform(&mut self, middleware: impl TransformMiddleware + 'static) {
31 self.transforms.clear();
32 self.transforms.push(Box::new(middleware));
33 }
34
35 pub fn register_renderer(&mut self, chart_type: &str, renderer: impl ChartRenderer + 'static) {
36 self.renderers.insert(chart_type.to_string(), Box::new(renderer));
37 }
38
39 pub fn set_datasource_resolver(&mut self, resolver: impl DatasourceResolver + 'static) {
40 self.datasource_resolver = Some(Box::new(resolver));
41 }
42
43 pub fn get_renderer(&self, chart_type: &str) -> Option<&dyn ChartRenderer> {
44 self.renderers.get(chart_type).map(|r| r.as_ref())
45 }
46
47 pub fn get_data_source(&self, name: &str) -> Option<&dyn DataSource> {
48 self.data_sources.get(name).map(|s| s.as_ref())
49 }
50
51 pub fn get_transform(&self) -> Option<&dyn TransformMiddleware> {
53 self.transforms.first().map(|t| t.as_ref())
54 }
55
56 pub fn has_renderer(&self, chart_type: &str) -> bool {
57 self.renderers.contains_key(chart_type)
58 }
59
60 pub fn renderer_types(&self) -> Vec<String> {
61 self.renderers.keys().cloned().collect()
62 }
63}
64
65impl Default for ChartMLRegistry {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::data::DataTable;
75 use crate::element::{ChartElement, ViewBox};
76 use crate::error::ChartError;
77 use crate::plugin::ChartConfig;
78
79 struct MockRenderer;
81
82 impl ChartRenderer for MockRenderer {
83 fn render(&self, _data: &DataTable, _config: &ChartConfig) -> Result<ChartElement, ChartError> {
84 Ok(ChartElement::Svg {
85 viewbox: ViewBox::new(0.0, 0.0, 800.0, 400.0),
86 width: Some(800.0),
87 height: Some(400.0),
88 class: "mock".to_string(),
89 children: vec![],
90 })
91 }
92 }
93
94 #[test]
95 fn registry_register_and_lookup_renderer() {
96 let mut registry = ChartMLRegistry::new();
97 registry.register_renderer("bar", MockRenderer);
98 assert!(registry.has_renderer("bar"));
99 assert!(!registry.has_renderer("pie"));
100 }
101
102 #[test]
103 fn registry_renderer_types() {
104 let mut registry = ChartMLRegistry::new();
105 registry.register_renderer("bar", MockRenderer);
106 registry.register_renderer("line", MockRenderer);
107 let types = registry.renderer_types();
108 assert!(types.contains(&"bar".to_string()));
109 assert!(types.contains(&"line".to_string()));
110 }
111
112 #[test]
113 fn registry_default() {
114 let registry = ChartMLRegistry::default();
115 assert!(registry.renderer_types().is_empty());
116 }
117}