Skip to main content

fastskill_core/search/
mod.rs

1//! Search domain module providing unified search capabilities across local and remote scopes
2//!
3//! This module provides a unified interface for searching skills across different scopes:
4//! - Local: Search installed/local skills using vector embeddings or text search
5//! - Remote: Search remote skill catalogs across configured repositories
6//!
7//! The module is designed to be CLI-agnostic and can be used by other entry points
8//! like HTTP API endpoints.
9
10pub mod local;
11pub mod remote;
12
13use serde::Serialize;
14use std::fmt;
15
16/// Search scope determines where to search for skills
17#[derive(Debug, Clone, PartialEq)]
18pub enum SearchScope {
19    /// Search local/installed skills
20    Local,
21    /// Search remote skill catalogs (all repositories)
22    Remote,
23    /// Search remote skill catalogs limited to specific repository
24    RemoteRepo(String),
25}
26
27/// Search query configuration
28#[derive(Debug, Clone)]
29pub struct SearchQuery {
30    /// The search query string
31    pub query: String,
32    /// Search scope (local or remote)
33    pub scope: SearchScope,
34    /// Maximum number of results to return
35    pub limit: usize,
36    /// Whether to use embedding search (for local search only)
37    pub embedding: Option<bool>,
38}
39
40/// Unified search result item that works across all search scopes
41#[derive(Debug, Clone, Serialize)]
42pub struct SearchResultItem {
43    /// Skill identifier
44    pub id: String,
45    /// Display name or title
46    pub name: String,
47    /// Optional description
48    pub description: Option<String>,
49    /// Source of the result (e.g., "local" or repository name)
50    pub source: String,
51    /// Optional similarity score (for embedding-based searches)
52    pub similarity: Option<f32>,
53    /// Optional skill path (for local results)
54    pub path: Option<String>,
55    /// Optional repository name (for remote results)
56    pub repository: Option<String>,
57}
58
59impl fmt::Display for SearchResultItem {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        write!(
62            f,
63            "{}: {}",
64            self.name,
65            self.description.as_deref().unwrap_or("No description")
66        )
67    }
68}
69
70/// Search-specific error types
71#[derive(Debug, thiserror::Error)]
72pub enum SearchError {
73    #[error("Configuration error: {0}")]
74    Config(String),
75    #[error("Validation error: {0}")]
76    Validation(String),
77    #[error("Service error: {0}")]
78    Service(#[from] crate::ServiceError),
79    #[error("Repository error: {0}")]
80    Repository(String),
81}
82
83/// Execute a search query and return unified results
84pub async fn execute(
85    query: SearchQuery,
86    service: &crate::FastSkillService,
87) -> Result<Vec<SearchResultItem>, SearchError> {
88    let scope = query.scope.clone();
89    match scope {
90        SearchScope::Local => local::execute_local_search(query, service).await,
91        SearchScope::Remote => remote::execute_remote_search(query, None).await,
92        SearchScope::RemoteRepo(repo_name) => {
93            remote::execute_remote_search(query, Some(repo_name)).await
94        }
95    }
96}