mecha10_cli/services/
component_catalog.rs

1#![allow(dead_code)]
2
3//! Component catalog service for managing available components
4//!
5//! This service provides a centralized interface for querying and managing
6//! the catalog of available components (drivers, packages, behaviors, stacks)
7//! that can be added to a Mecha10 project.
8
9use crate::component_catalog::{Component, ComponentCatalog, ComponentType};
10use anyhow::Result;
11
12/// Component catalog service for managing available components
13///
14/// # Examples
15///
16/// ```rust,ignore
17/// use mecha10_cli::services::ComponentCatalogService;
18///
19/// # fn example() -> anyhow::Result<()> {
20/// let service = ComponentCatalogService::new();
21///
22/// // Get all drivers
23/// let drivers = service.get_drivers();
24/// for driver in drivers {
25///     println!("Driver: {} - {}", driver.name, driver.description);
26/// }
27///
28/// // Search for camera components
29/// let cameras = service.search("camera");
30///
31/// // Get a specific component
32/// if let Some(component) = service.get("realsense-camera") {
33///     println!("Found: {}", component.name);
34/// }
35/// # Ok(())
36/// # }
37/// ```
38pub struct ComponentCatalogService {
39    catalog: ComponentCatalog,
40}
41
42impl ComponentCatalogService {
43    /// Create a new component catalog service
44    ///
45    /// Initializes the catalog with all built-in components.
46    pub fn new() -> Self {
47        Self {
48            catalog: ComponentCatalog::new(),
49        }
50    }
51
52    /// Get all available components
53    ///
54    /// Returns a vector of all components in the catalog.
55    pub fn get_all(&self) -> Vec<&Component> {
56        self.catalog.get_all()
57    }
58
59    /// Get all driver components
60    ///
61    /// Returns only components of type Driver.
62    pub fn get_drivers(&self) -> Vec<&Component> {
63        self.catalog.get_by_type(ComponentType::Driver)
64    }
65
66    /// Get all package components
67    ///
68    /// Returns only components of type Package.
69    pub fn get_packages(&self) -> Vec<&Component> {
70        self.catalog.get_by_type(ComponentType::Package)
71    }
72
73    /// Get all behavior components
74    ///
75    /// Returns only components of type Behavior.
76    pub fn get_behaviors(&self) -> Vec<&Component> {
77        self.catalog.get_by_type(ComponentType::Behavior)
78    }
79
80    /// Get all stack components
81    ///
82    /// Returns only components of type Stack (pre-configured sets).
83    pub fn get_stacks(&self) -> Vec<&Component> {
84        self.catalog.get_by_type(ComponentType::Stack)
85    }
86
87    /// Get components by type
88    ///
89    /// # Arguments
90    ///
91    /// * `component_type` - Type of components to retrieve
92    pub fn get_by_type(&self, component_type: ComponentType) -> Vec<&Component> {
93        self.catalog.get_by_type(component_type)
94    }
95
96    /// Get a specific component by ID
97    ///
98    /// # Arguments
99    ///
100    /// * `id` - Component identifier (e.g., "realsense-camera", "rplidar")
101    ///
102    /// # Returns
103    ///
104    /// Some(&Component) if found, None otherwise
105    pub fn get(&self, id: &str) -> Option<&Component> {
106        self.catalog.get(id)
107    }
108
109    /// Search components by query string
110    ///
111    /// Searches component names, descriptions, and tags.
112    ///
113    /// # Arguments
114    ///
115    /// * `query` - Search query (case-insensitive)
116    ///
117    /// # Returns
118    ///
119    /// Vector of components matching the query
120    pub fn search(&self, query: &str) -> Vec<&Component> {
121        self.catalog.search(query)
122    }
123
124    /// List components by category
125    ///
126    /// # Arguments
127    ///
128    /// * `category` - Category name (e.g., "vision", "motion", "sensors")
129    ///
130    /// # Returns
131    ///
132    /// Vector of components in the specified category
133    pub fn get_by_category(&self, category: &str) -> Vec<&Component> {
134        self.catalog
135            .get_all()
136            .into_iter()
137            .filter(|c| c.category == category)
138            .collect()
139    }
140
141    /// List all available categories
142    ///
143    /// # Returns
144    ///
145    /// Sorted vector of unique category names
146    pub fn list_categories(&self) -> Vec<String> {
147        let mut categories: Vec<String> = self.catalog.get_all().iter().map(|c| c.category.clone()).collect();
148
149        categories.sort();
150        categories.dedup();
151        categories
152    }
153
154    /// List all available tags
155    ///
156    /// # Returns
157    ///
158    /// Sorted vector of unique tags
159    pub fn list_tags(&self) -> Vec<String> {
160        let mut tags: Vec<String> = self
161            .catalog
162            .get_all()
163            .iter()
164            .flat_map(|c| c.tags.iter().cloned())
165            .collect();
166
167        tags.sort();
168        tags.dedup();
169        tags
170    }
171
172    /// Get components with a specific tag
173    ///
174    /// # Arguments
175    ///
176    /// * `tag` - Tag to filter by (e.g., "camera", "lidar", "imu")
177    pub fn get_by_tag(&self, tag: &str) -> Vec<&Component> {
178        self.catalog
179            .get_all()
180            .into_iter()
181            .filter(|c| c.tags.iter().any(|t| t == tag))
182            .collect()
183    }
184
185    /// Get component count by type
186    ///
187    /// # Returns
188    ///
189    /// Tuple of (drivers, packages, behaviors, stacks)
190    pub fn get_counts(&self) -> (usize, usize, usize, usize) {
191        let drivers = self.get_drivers().len();
192        let packages = self.get_packages().len();
193        let behaviors = self.get_behaviors().len();
194        let stacks = self.get_stacks().len();
195
196        (drivers, packages, behaviors, stacks)
197    }
198
199    /// Check if a component exists
200    ///
201    /// # Arguments
202    ///
203    /// * `id` - Component identifier
204    pub fn exists(&self, id: &str) -> bool {
205        self.catalog.get(id).is_some()
206    }
207
208    /// Get recommended components for a platform
209    ///
210    /// Returns components commonly used with a specific robot platform.
211    ///
212    /// # Arguments
213    ///
214    /// * `platform` - Platform name (e.g., "rover", "arm", "quadruped")
215    pub fn get_recommended_for_platform(&self, platform: &str) -> Vec<&Component> {
216        // This could be enhanced with platform-specific metadata
217        // For now, return popular components
218        match platform.to_lowercase().as_str() {
219            "rover" => self
220                .search("lidar")
221                .into_iter()
222                .chain(self.search("camera"))
223                .chain(self.search("imu"))
224                .collect(),
225            "arm" => self
226                .search("joint")
227                .into_iter()
228                .chain(self.search("gripper"))
229                .chain(self.search("camera"))
230                .collect(),
231            _ => vec![],
232        }
233    }
234
235    /// Validate component dependencies
236    ///
237    /// Check if all cargo dependencies for a component are valid.
238    ///
239    /// # Arguments
240    ///
241    /// * `component_id` - Component to validate
242    pub fn validate_dependencies(&self, component_id: &str) -> Result<Vec<String>> {
243        let component = self
244            .get(component_id)
245            .ok_or_else(|| anyhow::anyhow!("Component not found: {}", component_id))?;
246
247        let deps: Vec<String> = component
248            .cargo_dependencies
249            .iter()
250            .map(|dep| {
251                if let Some(version) = &dep.version {
252                    format!("{} = \"{}\"", dep.name, version)
253                } else if let Some(path) = &dep.path {
254                    format!("{} (path: {})", dep.name, path)
255                } else {
256                    dep.name.clone()
257                }
258            })
259            .collect();
260
261        Ok(deps)
262    }
263}
264
265impl Default for ComponentCatalogService {
266    fn default() -> Self {
267        Self::new()
268    }
269}