mecha10-cli-core 0.9.0

Mecha10 CLI core foundation — shared types, services, and utilities
Documentation
#![allow(dead_code)]

//! Component catalog service for managing available components
//!
//! Centralized interface for querying the catalog of drivers, packages, behaviors, and stacks.

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

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
    pub fn get_by_type(&self, component_type: ComponentType) -> Vec<&Component> {
        self.catalog.get_by_type(component_type)
    }

    /// Get a specific component by ID
    pub fn get(&self, id: &str) -> Option<&Component> {
        self.catalog.get(id)
    }

    /// Search component names, descriptions, and tags by a case-insensitive query.
    pub fn search(&self, query: &str) -> Vec<&Component> {
        self.catalog.search(query)
    }

    /// List components in the given category (e.g. "vision", "motion", "sensors")
    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, sorted and deduplicated
    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, sorted and deduplicated
    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 (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: (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
    pub fn exists(&self, id: &str) -> bool {
        self.catalog.get(id).is_some()
    }

    /// Get recommended components for a platform (e.g. "rover", "arm", "quadruped")
    pub fn get_recommended_for_platform(&self, platform: &str) -> Vec<&Component> {
        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(),
            "drone" => self
                .search("camera")
                .into_iter()
                .chain(self.search("imu"))
                .chain(self.search("flight"))
                .collect(),
            _ => vec![],
        }
    }

    /// Validate that all cargo dependencies for a component are well-formed.
    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()
    }
}