use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Instant;
use dashmap::DashMap;
use vibe_graph_core::SourceCodeGraph;
use vibe_graph_ops::Store;
#[derive(Clone)]
pub struct RegisteredProject {
pub name: String,
pub workspace_path: PathBuf,
pub graph: Arc<SourceCodeGraph>,
pub store: Store,
pub registered_at: Instant,
}
#[derive(Default)]
pub struct ProjectRegistry {
pub projects: DashMap<String, RegisteredProject>,
}
impl ProjectRegistry {
pub fn new() -> Self {
Self {
projects: DashMap::new(),
}
}
pub fn register(&self, project: RegisteredProject) {
self.projects.insert(project.name.clone(), project);
}
pub fn unregister(&self, name: &str) -> Option<RegisteredProject> {
self.projects.remove(name).map(|(_, v)| v)
}
pub fn get(
&self,
name: &str,
) -> Option<dashmap::mapref::one::Ref<'_, String, RegisteredProject>> {
self.projects.get(name)
}
pub fn list_names(&self) -> Vec<String> {
self.projects.iter().map(|r| r.key().clone()).collect()
}
pub fn len(&self) -> usize {
self.projects.len()
}
pub fn is_empty(&self) -> bool {
self.projects.is_empty()
}
pub fn get_single(&self) -> Option<dashmap::mapref::one::Ref<'_, String, RegisteredProject>> {
if self.projects.len() == 1 {
self.projects.iter().next().map(|r| {
let key = r.key().clone();
drop(r);
self.projects.get(&key).unwrap()
})
} else {
None
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegisterProjectRequest {
pub name: String,
pub workspace_path: PathBuf,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RegisterProjectResponse {
pub success: bool,
pub message: String,
pub project_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthResponse {
pub status: String,
pub version: String,
pub project_count: usize,
pub projects: Vec<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ProjectInfo {
pub name: String,
pub workspace_path: String,
pub node_count: usize,
pub edge_count: usize,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ListProjectsOutput {
pub projects: Vec<ProjectInfo>,
pub count: usize,
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct SearchNodesInput {
#[serde(default)]
pub project: Option<String>,
pub query: String,
#[serde(default)]
pub kind: Option<String>,
#[serde(default)]
pub extension: Option<String>,
#[serde(default = "default_limit")]
pub limit: usize,
}
fn default_limit() -> usize {
20
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GetDependenciesInput {
#[serde(default)]
pub project: Option<String>,
pub node_path: String,
#[serde(default = "default_true")]
pub incoming: bool,
#[serde(default = "default_true")]
pub outgoing: bool,
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ImpactAnalysisInput {
#[serde(default)]
pub project: Option<String>,
pub paths: Vec<String>,
#[serde(default = "default_depth")]
pub depth: usize,
#[serde(default = "default_true")]
pub include_tests: bool,
}
fn default_depth() -> usize {
2
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GetNodeContextInput {
#[serde(default)]
pub project: Option<String>,
pub node_path: String,
#[serde(default = "default_context_depth")]
pub depth: usize,
#[serde(default)]
pub include_content: bool,
}
fn default_context_depth() -> usize {
1
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct ListFilesInput {
#[serde(default)]
pub project: Option<String>,
#[serde(default)]
pub path: Option<String>,
#[serde(default)]
pub extension: Option<String>,
#[serde(default)]
pub kind: Option<String>,
#[serde(default = "default_limit")]
pub limit: usize,
}
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct GetGitChangesInput {
#[serde(default)]
pub project: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct NodeInfo {
pub id: u64,
pub name: String,
pub path: String,
pub kind: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub extension: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub metadata: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct EdgeInfo {
pub from: String,
pub to: String,
pub relationship: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct SearchNodesOutput {
pub nodes: Vec<NodeInfo>,
pub total_matches: usize,
pub query: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct GetDependenciesOutput {
pub node: NodeInfo,
pub dependents: Vec<NodeInfo>,
pub dependencies: Vec<NodeInfo>,
pub edges: Vec<EdgeInfo>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ImpactAnalysisOutput {
pub analyzed_paths: Vec<String>,
pub impacted_nodes: Vec<NodeInfo>,
pub impacted_tests: Vec<NodeInfo>,
pub impact_count: usize,
pub depth: usize,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct GitChangesOutput {
pub changes: Vec<GitFileChange>,
pub change_count: usize,
pub summary: GitChangesSummary,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct GitFileChange {
pub path: String,
pub kind: String,
pub staged: bool,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct GitChangesSummary {
pub modified: usize,
pub added: usize,
pub deleted: usize,
pub untracked: usize,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct NodeContextOutput {
pub node: NodeInfo,
pub neighbors: Vec<NodeInfo>,
pub edges: Vec<EdgeInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ListFilesOutput {
pub files: Vec<NodeInfo>,
pub total: usize,
pub path: Option<String>,
}