Skip to main content

dampen_cli/commands/check/
custom_widgets.rs

1use serde::{Deserialize, Serialize};
2use std::collections::{HashMap, HashSet};
3use std::fs;
4use std::path::Path;
5
6use super::errors::CheckError;
7
8/// Configuration for a custom widget's allowed attributes.
9#[derive(Debug, Clone, Serialize, Deserialize, Default)]
10pub struct CustomWidgetConfig {
11    #[serde(default)]
12    pub allowed_attributes: HashSet<String>,
13}
14
15/// Registry of custom widget configurations.
16#[derive(Debug, Clone, Default)]
17pub struct CustomWidgetRegistry {
18    widgets: HashMap<String, CustomWidgetConfig>,
19}
20
21impl CustomWidgetRegistry {
22    /// Creates a new empty registry.
23    pub fn new() -> Self {
24        Self {
25            widgets: HashMap::new(),
26        }
27    }
28
29    /// Loads a custom widget registry from a JSON file.
30    ///
31    /// # Arguments
32    ///
33    /// * `path` - Path to the JSON file containing widget configurations
34    ///
35    /// # Returns
36    ///
37    /// A `Result` containing the registry or a `CheckError` if loading fails.
38    ///
39    /// # Example JSON format
40    ///
41    /// ```json
42    /// {
43    ///   "CustomWidget": {
44    ///     "allowed_attributes": ["value", "mode", "format"]
45    ///   },
46    ///   "DataGrid": {
47    ///     "allowed_attributes": ["columns", "rows", "sortable"]
48    ///   }
49    /// }
50    /// ```
51    #[allow(clippy::result_large_err)]
52    pub fn load_from_json(path: &Path) -> Result<Self, CheckError> {
53        let content = fs::read_to_string(path).map_err(CheckError::Io)?;
54        let widgets: HashMap<String, CustomWidgetConfig> =
55            serde_json::from_str(&content).map_err(|e| {
56                CheckError::CustomWidgetConfigLoadError {
57                    path: path.to_path_buf(),
58                    source: e,
59                }
60            })?;
61
62        Ok(Self { widgets })
63    }
64
65    /// Checks if a widget is registered.
66    pub fn has_widget(&self, widget_name: &str) -> bool {
67        self.widgets.contains_key(widget_name)
68    }
69
70    /// Checks if an attribute is allowed for a custom widget.
71    ///
72    /// # Arguments
73    ///
74    /// * `widget_name` - Name of the custom widget
75    /// * `attribute` - Name of the attribute to check
76    ///
77    /// # Returns
78    ///
79    /// `true` if the attribute is allowed, `false` otherwise.
80    /// If the widget is not registered, returns `false`.
81    pub fn is_attribute_allowed(&self, widget_name: &str, attribute: &str) -> bool {
82        self.widgets
83            .get(widget_name)
84            .map(|config| config.allowed_attributes.contains(attribute))
85            .unwrap_or(false)
86    }
87
88    /// Gets all allowed attributes for a custom widget.
89    ///
90    /// # Arguments
91    ///
92    /// * `widget_name` - Name of the custom widget
93    ///
94    /// # Returns
95    ///
96    /// A vector of attribute names, or an empty vector if the widget is not registered.
97    pub fn get_allowed_attributes(&self, widget_name: &str) -> Vec<&str> {
98        self.widgets
99            .get(widget_name)
100            .map(|config| {
101                config
102                    .allowed_attributes
103                    .iter()
104                    .map(|s| s.as_str())
105                    .collect()
106            })
107            .unwrap_or_default()
108    }
109
110    /// Adds a custom widget configuration.
111    pub fn add_widget(&mut self, name: String, config: CustomWidgetConfig) {
112        self.widgets.insert(name, config);
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_empty_registry() {
122        let registry = CustomWidgetRegistry::new();
123        assert!(!registry.has_widget("CustomWidget"));
124        assert!(!registry.is_attribute_allowed("CustomWidget", "value"));
125    }
126
127    #[test]
128    fn test_add_widget() {
129        let mut registry = CustomWidgetRegistry::new();
130        let mut config = CustomWidgetConfig::default();
131        config.allowed_attributes.insert("value".to_string());
132        config.allowed_attributes.insert("mode".to_string());
133
134        registry.add_widget("CustomWidget".to_string(), config);
135
136        assert!(registry.has_widget("CustomWidget"));
137        assert!(registry.is_attribute_allowed("CustomWidget", "value"));
138        assert!(registry.is_attribute_allowed("CustomWidget", "mode"));
139        assert!(!registry.is_attribute_allowed("CustomWidget", "unknown"));
140    }
141
142    #[test]
143    fn test_get_allowed_attributes() {
144        let mut registry = CustomWidgetRegistry::new();
145        let mut config = CustomWidgetConfig::default();
146        config.allowed_attributes.insert("value".to_string());
147        config.allowed_attributes.insert("mode".to_string());
148
149        registry.add_widget("CustomWidget".to_string(), config);
150
151        let attrs = registry.get_allowed_attributes("CustomWidget");
152        assert_eq!(attrs.len(), 2);
153        assert!(attrs.contains(&"value"));
154        assert!(attrs.contains(&"mode"));
155    }
156
157    #[test]
158    fn test_get_allowed_attributes_unknown_widget() {
159        let registry = CustomWidgetRegistry::new();
160        let attrs = registry.get_allowed_attributes("UnknownWidget");
161        assert!(attrs.is_empty());
162    }
163}