use serde::{Deserialize, Serialize};
use strum::{EnumString, Display};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct Span {
pub start_line: usize,
pub end_line: usize,
}
impl Span {
pub fn new(start_line: usize, start_col: usize, end_line: usize, end_col: usize) -> Self {
let _ = (start_col, end_col);
Self {
start_line,
end_line,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, EnumString, Display)]
#[strum(serialize_all = "PascalCase")]
pub enum SymbolKind {
Function,
Class,
Struct,
Enum,
Interface,
Trait,
Constant,
Variable,
Method,
Module,
Namespace,
Type,
Macro,
Property,
Event,
Import,
Export,
Attribute,
#[strum(default)]
Unknown(String),
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)]
#[serde(rename_all = "lowercase")]
pub enum Language {
#[default]
Rust,
Python,
JavaScript,
TypeScript,
Vue,
Svelte,
Go,
Java,
PHP,
C,
Cpp,
CSharp,
Ruby,
Kotlin,
Swift,
Zig,
Unknown,
}
impl Language {
pub fn from_extension(ext: &str) -> Self {
match ext {
"rs" => Language::Rust,
"py" => Language::Python,
"js" | "mjs" | "cjs" | "jsx" => Language::JavaScript,
"ts" | "mts" | "cts" | "tsx" => Language::TypeScript,
"vue" => Language::Vue,
"svelte" => Language::Svelte,
"go" => Language::Go,
"java" => Language::Java,
"php" => Language::PHP,
"c" | "h" => Language::C,
"cpp" | "cc" | "cxx" | "hpp" | "hxx" | "C" | "H" => Language::Cpp,
"cs" => Language::CSharp,
"rb" | "rake" | "gemspec" => Language::Ruby,
"kt" | "kts" => Language::Kotlin,
"swift" => Language::Swift,
"zig" => Language::Zig,
_ => Language::Unknown,
}
}
pub fn is_supported(&self) -> bool {
match self {
Language::Rust => true,
Language::TypeScript => true,
Language::JavaScript => true,
Language::Vue => true,
Language::Svelte => true,
Language::Python => true,
Language::Go => true,
Language::Java => true,
Language::PHP => true,
Language::C => true,
Language::Cpp => true,
Language::CSharp => true,
Language::Ruby => true,
Language::Kotlin => true,
Language::Swift => false, Language::Zig => true,
Language::Unknown => false,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ImportType {
Internal,
External,
Stdlib,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DependencyInfo {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub line: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbols: Option<Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct Dependency {
pub file_id: i64,
pub imported_path: String,
pub resolved_file_id: Option<i64>,
pub import_type: ImportType,
pub line_number: usize,
pub imported_symbols: Option<Vec<String>>,
}
fn is_unknown_kind(kind: &SymbolKind) -> bool {
matches!(kind, SymbolKind::Unknown(_))
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub path: String,
#[serde(skip)]
pub lang: Language,
#[serde(skip_serializing_if = "is_unknown_kind")]
pub kind: SymbolKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
pub span: Span,
pub preview: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub dependencies: Option<Vec<DependencyInfo>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MatchResult {
#[serde(skip_serializing_if = "is_unknown_kind")]
pub kind: SymbolKind,
#[serde(skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
pub span: Span,
pub preview: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub context_before: Vec<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub context_after: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileGroupedResult {
pub path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub dependencies: Option<Vec<DependencyInfo>>,
pub matches: Vec<MatchResult>,
}
impl SearchResult {
pub fn new(
path: String,
lang: Language,
kind: SymbolKind,
symbol: Option<String>,
span: Span,
scope: Option<String>,
preview: String,
) -> Self {
let _ = scope;
Self {
path,
lang,
kind,
symbol,
span,
preview,
dependencies: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexConfig {
pub languages: Vec<Language>,
pub include_patterns: Vec<String>,
pub exclude_patterns: Vec<String>,
pub follow_symlinks: bool,
pub max_file_size: usize,
pub parallel_threads: usize,
pub query_timeout_secs: u64,
}
impl Default for IndexConfig {
fn default() -> Self {
Self {
languages: vec![],
include_patterns: vec![],
exclude_patterns: vec![],
follow_symlinks: false,
max_file_size: 10 * 1024 * 1024, parallel_threads: 0, query_timeout_secs: 30, }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexStats {
pub total_files: usize,
pub index_size_bytes: u64,
pub last_updated: String,
pub files_by_language: std::collections::HashMap<String, usize>,
pub lines_by_language: std::collections::HashMap<String, usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexedFile {
pub path: String,
pub language: String,
pub last_indexed: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum IndexStatus {
Fresh,
Stale,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexWarning {
pub reason: String,
pub action_required: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<IndexWarningDetails>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IndexWarningDetails {
#[serde(skip_serializing_if = "Option::is_none")]
pub current_branch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub indexed_branch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub current_commit: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub indexed_commit: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PaginationInfo {
pub total: usize,
pub count: usize,
pub offset: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<usize>,
pub has_more: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub ai_instruction: Option<String>,
pub status: IndexStatus,
pub can_trust_results: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub warning: Option<IndexWarning>,
pub pagination: PaginationInfo,
pub results: Vec<FileGroupedResult>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompactionReport {
pub files_removed: usize,
pub space_saved_bytes: u64,
pub duration_ms: u64,
}