mecha10-cli 0.1.47

Mecha10 CLI tool
Documentation
#![allow(dead_code)]

//! Component catalog service for managing available components
//!
//! This service provides a centralized interface for querying and managing
//! the catalog of available components (drivers, packages, behaviors, stacks)
//! that can be added to a Mecha10 project.

use crate::component_catalog::{Component, ComponentCatalog, ComponentType};
use anyhow::Result;

/// Component catalog service for managing available components
///
/// # Examples
///
/// ```rust,ignore
/// use mecha10_cli::services::ComponentCatalogService;
///
/// # fn example() -> anyhow::Result<()> {
/// let service = ComponentCatalogService::new();
///
/// // Get all drivers
/// let drivers = service.get_drivers();
/// for driver in drivers {
///     println!("Driver: {} - {}", driver.name, driver.description);
/// }
///
/// // Search for camera components
/// let cameras = service.search("camera");
///
/// // Get a specific component
/// if let Some(component) = service.get("realsense-camera") {
///     println!("Found: {}", component.name);
/// }
/// # Ok(())
/// # }
/// ```
pub struct ComponentCatalogService {
    catalog: ComponentCatalog,
}

impl ComponentCatalogService {
    /// Create a new component catalog service
    ///
    /// Initializes the catalog with all built-in components.
    pub fn new() -> Self {
        Self {
            catalog: ComponentCatalog::new(),
        }
    }

    /// Get all available components
    ///
    /// Returns a vector of all components in the catalog.
    pub fn get_all(&self) -> Vec<&Component> {
        self.catalog.get_all()
    }

    /// Get all driver components
    ///
    /// Returns only components of type Driver.
    pub fn get_drivers(&self) -> Vec<&Component> {
        self.catalog.get_by_type(ComponentType::Driver)
    }

    /// Get all package components
    ///
    /// Returns only components of type Package.
    pub fn get_packages(&self) -> Vec<&Component> {
        self.catalog.get_by_type(ComponentType::Package)
    }

    /// Get all behavior components
    ///
    /// Returns only components of type Behavior.
    pub fn get_behaviors(&self) -> Vec<&Component> {
        self.catalog.get_by_type(ComponentType::Behavior)
    }

    /// Get all stack components
    ///
    /// Returns only components of type Stack (pre-configured sets).
    pub fn get_stacks(&self) -> Vec<&Component> {
        self.catalog.get_by_type(ComponentType::Stack)
    }

    /// Get components by type
    ///
    /// # Arguments
    ///
    /// * `component_type` - Type of components to retrieve
    pub fn get_by_type(&self, component_type: ComponentType) -> Vec<&Component> {
        self.catalog.get_by_type(component_type)
    }

    /// Get a specific component by ID
    ///
    /// # Arguments
    ///
    /// * `id` - Component identifier (e.g., "realsense-camera", "rplidar")
    ///
    /// # Returns
    ///
    /// Some(&Component) if found, None otherwise
    pub fn get(&self, id: &str) -> Option<&Component> {
        self.catalog.get(id)
    }

    /// Search components by query string
    ///
    /// Searches component names, descriptions, and tags.
    ///
    /// # Arguments
    ///
    /// * `query` - Search query (case-insensitive)
    ///
    /// # Returns
    ///
    /// Vector of components matching the query
    pub fn search(&self, query: &str) -> Vec<&Component> {
        self.catalog.search(query)
    }

    /// List components by category
    ///
    /// # Arguments
    ///
    /// * `category` - Category name (e.g., "vision", "motion", "sensors")
    ///
    /// # Returns
    ///
    /// Vector of components in the specified category
    pub fn get_by_category(&self, category: &str) -> Vec<&Component> {
        self.catalog
            .get_all()
            .into_iter()
            .filter(|c| c.category == category)
            .collect()
    }

    /// List all available categories
    ///
    /// # Returns
    ///
    /// Sorted vector of unique category names
    pub fn list_categories(&self) -> Vec<String> {
        let mut categories: Vec<String> = self.catalog.get_all().iter().map(|c| c.category.clone()).collect();

        categories.sort();
        categories.dedup();
        categories
    }

    /// List all available tags
    ///
    /// # Returns
    ///
    /// Sorted vector of unique tags
    pub fn list_tags(&self) -> Vec<String> {
        let mut tags: Vec<String> = self
            .catalog
            .get_all()
            .iter()
            .flat_map(|c| c.tags.iter().cloned())
            .collect();

        tags.sort();
        tags.dedup();
        tags
    }

    /// Get components with a specific tag
    ///
    /// # Arguments
    ///
    /// * `tag` - Tag to filter by (e.g., "camera", "lidar", "imu")
    pub fn get_by_tag(&self, tag: &str) -> Vec<&Component> {
        self.catalog
            .get_all()
            .into_iter()
            .filter(|c| c.tags.iter().any(|t| t == tag))
            .collect()
    }

    /// Get component count by type
    ///
    /// # Returns
    ///
    /// Tuple of (drivers, packages, behaviors, stacks)
    pub fn get_counts(&self) -> (usize, usize, usize, usize) {
        let drivers = self.get_drivers().len();
        let packages = self.get_packages().len();
        let behaviors = self.get_behaviors().len();
        let stacks = self.get_stacks().len();

        (drivers, packages, behaviors, stacks)
    }

    /// Check if a component exists
    ///
    /// # Arguments
    ///
    /// * `id` - Component identifier
    pub fn exists(&self, id: &str) -> bool {
        self.catalog.get(id).is_some()
    }

    /// Get recommended components for a platform
    ///
    /// Returns components commonly used with a specific robot platform.
    ///
    /// # Arguments
    ///
    /// * `platform` - Platform name (e.g., "rover", "arm", "quadruped")
    pub fn get_recommended_for_platform(&self, platform: &str) -> Vec<&Component> {
        // This could be enhanced with platform-specific metadata
        // For now, return popular components
        match platform.to_lowercase().as_str() {
            "rover" => self
                .search("lidar")
                .into_iter()
                .chain(self.search("camera"))
                .chain(self.search("imu"))
                .collect(),
            "arm" => self
                .search("joint")
                .into_iter()
                .chain(self.search("gripper"))
                .chain(self.search("camera"))
                .collect(),
            _ => vec![],
        }
    }

    /// Validate component dependencies
    ///
    /// Check if all cargo dependencies for a component are valid.
    ///
    /// # Arguments
    ///
    /// * `component_id` - Component to validate
    pub fn validate_dependencies(&self, component_id: &str) -> Result<Vec<String>> {
        let component = self
            .get(component_id)
            .ok_or_else(|| anyhow::anyhow!("Component not found: {}", component_id))?;

        let deps: Vec<String> = component
            .cargo_dependencies
            .iter()
            .map(|dep| {
                if let Some(version) = &dep.version {
                    format!("{} = \"{}\"", dep.name, version)
                } else if let Some(path) = &dep.path {
                    format!("{} (path: {})", dep.name, path)
                } else {
                    dep.name.clone()
                }
            })
            .collect();

        Ok(deps)
    }
}

impl Default for ComponentCatalogService {
    fn default() -> Self {
        Self::new()
    }
}