armature_admin/
registry.rs

1//! Model registry for admin
2
3use crate::model::ModelDefinition;
4use std::collections::HashMap;
5
6/// Registry of admin models
7#[derive(Debug, Default)]
8pub struct ModelRegistry {
9    /// Registered models by name
10    models: HashMap<String, ModelDefinition>,
11    /// Model ordering (for sidebar)
12    order: Vec<String>,
13    /// Model groups
14    groups: HashMap<String, Vec<String>>,
15}
16
17impl ModelRegistry {
18    /// Create a new registry
19    pub fn new() -> Self {
20        Self {
21            models: HashMap::new(),
22            order: Vec::new(),
23            groups: HashMap::new(),
24        }
25    }
26
27    /// Register a model
28    pub fn register(&mut self, model: ModelDefinition) {
29        let name = model.name.clone();
30        self.order.push(name.clone());
31        self.models.insert(name, model);
32    }
33
34    /// Register a model in a group
35    pub fn register_in_group(&mut self, group: impl Into<String>, model: ModelDefinition) {
36        let group = group.into();
37        let name = model.name.clone();
38
39        self.models.insert(name.clone(), model);
40
41        if !self.order.contains(&name) {
42            self.order.push(name.clone());
43        }
44
45        self.groups
46            .entry(group)
47            .or_default()
48            .push(name);
49    }
50
51    /// Get a model by name
52    pub fn get(&self, name: &str) -> Option<&ModelDefinition> {
53        self.models.get(name)
54    }
55
56    /// Get all models
57    pub fn all(&self) -> Vec<&ModelDefinition> {
58        self.order
59            .iter()
60            .filter_map(|name| self.models.get(name))
61            .collect()
62    }
63
64    /// Get model names in order
65    pub fn names(&self) -> &[String] {
66        &self.order
67    }
68
69    /// Get models in a group
70    pub fn group(&self, name: &str) -> Vec<&ModelDefinition> {
71        self.groups
72            .get(name)
73            .map(|names| {
74                names
75                    .iter()
76                    .filter_map(|n| self.models.get(n))
77                    .collect()
78            })
79            .unwrap_or_default()
80    }
81
82    /// Get all groups
83    pub fn groups(&self) -> &HashMap<String, Vec<String>> {
84        &self.groups
85    }
86
87    /// Check if a model exists
88    pub fn contains(&self, name: &str) -> bool {
89        self.models.contains_key(name)
90    }
91
92    /// Get model count
93    pub fn count(&self) -> usize {
94        self.models.len()
95    }
96
97    /// Remove a model
98    pub fn unregister(&mut self, name: &str) -> Option<ModelDefinition> {
99        self.order.retain(|n| n != name);
100        for group in self.groups.values_mut() {
101            group.retain(|n| n != name);
102        }
103        self.models.remove(name)
104    }
105
106    /// Get models for sidebar navigation
107    pub fn sidebar_items(&self) -> Vec<SidebarItem> {
108        // If there are groups, organize by group
109        if !self.groups.is_empty() {
110            let mut items = Vec::new();
111            let mut ungrouped = Vec::new();
112
113            // Add grouped models
114            for (group_name, model_names) in &self.groups {
115                let models: Vec<_> = model_names
116                    .iter()
117                    .filter_map(|n| self.models.get(n))
118                    .map(|m| SidebarItem::Model {
119                        name: m.name.clone(),
120                        label: m.verbose_name.clone(),
121                        icon: m.icon.clone(),
122                    })
123                    .collect();
124
125                if !models.is_empty() {
126                    items.push(SidebarItem::Group {
127                        name: group_name.clone(),
128                        items: models,
129                    });
130                }
131            }
132
133            // Add ungrouped models
134            for name in &self.order {
135                let in_group = self.groups.values().any(|g| g.contains(name));
136                if !in_group {
137                    if let Some(model) = self.models.get(name) {
138                        ungrouped.push(SidebarItem::Model {
139                            name: model.name.clone(),
140                            label: model.verbose_name.clone(),
141                            icon: model.icon.clone(),
142                        });
143                    }
144                }
145            }
146
147            items.extend(ungrouped);
148            items
149        } else {
150            // Just list all models
151            self.order
152                .iter()
153                .filter_map(|name| self.models.get(name))
154                .map(|m| SidebarItem::Model {
155                    name: m.name.clone(),
156                    label: m.verbose_name.clone(),
157                    icon: m.icon.clone(),
158                })
159                .collect()
160        }
161    }
162}
163
164/// Sidebar navigation item
165#[derive(Debug, Clone)]
166pub enum SidebarItem {
167    /// Single model
168    Model {
169        name: String,
170        label: String,
171        icon: Option<String>,
172    },
173    /// Group of models
174    Group {
175        name: String,
176        items: Vec<SidebarItem>,
177    },
178    /// Divider
179    Divider,
180    /// Custom link
181    Link {
182        label: String,
183        url: String,
184        icon: Option<String>,
185    },
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191    use crate::field::{FieldDefinition, FieldType};
192
193    #[test]
194    fn test_registry() {
195        let mut registry = ModelRegistry::new();
196
197        let user = ModelDefinition::builder("user")
198            .id_field()
199            .field(FieldDefinition::new("name", FieldType::String))
200            .build();
201
202        let post = ModelDefinition::builder("post")
203            .id_field()
204            .field(FieldDefinition::new("title", FieldType::String))
205            .build();
206
207        registry.register(user);
208        registry.register(post);
209
210        assert_eq!(registry.count(), 2);
211        assert!(registry.contains("user"));
212        assert!(registry.contains("post"));
213        assert!(!registry.contains("comment"));
214    }
215
216    #[test]
217    fn test_registry_groups() {
218        let mut registry = ModelRegistry::new();
219
220        let user = ModelDefinition::builder("user")
221            .id_field()
222            .build();
223
224        let role = ModelDefinition::builder("role")
225            .id_field()
226            .build();
227
228        registry.register_in_group("Auth", user);
229        registry.register_in_group("Auth", role);
230
231        let auth_models = registry.group("Auth");
232        assert_eq!(auth_models.len(), 2);
233    }
234}
235