1use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::path::PathBuf;
7use std::sync::Arc;
8use std::time::Instant;
9
10use dashmap::DashMap;
11use vibe_graph_core::SourceCodeGraph;
12use vibe_graph_ops::Store;
13
14#[derive(Clone)]
20pub struct RegisteredProject {
21 pub name: String,
23
24 pub workspace_path: PathBuf,
26
27 pub graph: Arc<SourceCodeGraph>,
29
30 pub store: Store,
32
33 pub registered_at: Instant,
35}
36
37#[derive(Default)]
39pub struct ProjectRegistry {
40 pub projects: DashMap<String, RegisteredProject>,
42}
43
44impl ProjectRegistry {
45 pub fn new() -> Self {
47 Self {
48 projects: DashMap::new(),
49 }
50 }
51
52 pub fn register(&self, project: RegisteredProject) {
54 self.projects.insert(project.name.clone(), project);
55 }
56
57 pub fn unregister(&self, name: &str) -> Option<RegisteredProject> {
59 self.projects.remove(name).map(|(_, v)| v)
60 }
61
62 pub fn get(
64 &self,
65 name: &str,
66 ) -> Option<dashmap::mapref::one::Ref<'_, String, RegisteredProject>> {
67 self.projects.get(name)
68 }
69
70 pub fn list_names(&self) -> Vec<String> {
72 self.projects.iter().map(|r| r.key().clone()).collect()
73 }
74
75 pub fn len(&self) -> usize {
77 self.projects.len()
78 }
79
80 pub fn is_empty(&self) -> bool {
82 self.projects.is_empty()
83 }
84
85 pub fn get_single(&self) -> Option<dashmap::mapref::one::Ref<'_, String, RegisteredProject>> {
87 if self.projects.len() == 1 {
88 self.projects.iter().next().map(|r| {
89 let key = r.key().clone();
91 drop(r);
92 self.projects.get(&key).unwrap()
93 })
94 } else {
95 None
96 }
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct RegisterProjectRequest {
103 pub name: String,
105
106 pub workspace_path: PathBuf,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct RegisterProjectResponse {
113 pub success: bool,
115
116 pub message: String,
118
119 pub project_count: usize,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct HealthResponse {
126 pub status: String,
128
129 pub version: String,
131
132 pub project_count: usize,
134
135 pub projects: Vec<String>,
137}
138
139#[derive(Debug, Clone, Serialize, JsonSchema)]
141pub struct ProjectInfo {
142 pub name: String,
144
145 pub workspace_path: String,
147
148 pub node_count: usize,
150
151 pub edge_count: usize,
153}
154
155#[derive(Debug, Clone, Serialize, JsonSchema)]
157pub struct ListProjectsOutput {
158 pub projects: Vec<ProjectInfo>,
160
161 pub count: usize,
163}
164
165#[derive(Debug, Clone, Deserialize, JsonSchema)]
171pub struct SearchNodesInput {
172 #[serde(default)]
175 pub project: Option<String>,
176
177 pub query: String,
179
180 #[serde(default)]
182 pub kind: Option<String>,
183
184 #[serde(default)]
186 pub extension: Option<String>,
187
188 #[serde(default = "default_limit")]
190 pub limit: usize,
191}
192
193fn default_limit() -> usize {
194 20
195}
196
197#[derive(Debug, Clone, Deserialize, JsonSchema)]
199pub struct GetDependenciesInput {
200 #[serde(default)]
202 pub project: Option<String>,
203
204 pub node_path: String,
206
207 #[serde(default = "default_true")]
209 pub incoming: bool,
210
211 #[serde(default = "default_true")]
213 pub outgoing: bool,
214}
215
216fn default_true() -> bool {
217 true
218}
219
220#[derive(Debug, Clone, Deserialize, JsonSchema)]
222pub struct ImpactAnalysisInput {
223 #[serde(default)]
225 pub project: Option<String>,
226
227 pub paths: Vec<String>,
229
230 #[serde(default = "default_depth")]
232 pub depth: usize,
233
234 #[serde(default = "default_true")]
236 pub include_tests: bool,
237}
238
239fn default_depth() -> usize {
240 2
241}
242
243#[derive(Debug, Clone, Deserialize, JsonSchema)]
245pub struct GetNodeContextInput {
246 #[serde(default)]
248 pub project: Option<String>,
249
250 pub node_path: String,
252
253 #[serde(default = "default_context_depth")]
255 pub depth: usize,
256
257 #[serde(default)]
259 pub include_content: bool,
260}
261
262fn default_context_depth() -> usize {
263 1
264}
265
266#[derive(Debug, Clone, Deserialize, JsonSchema)]
268pub struct ListFilesInput {
269 #[serde(default)]
271 pub project: Option<String>,
272
273 #[serde(default)]
275 pub path: Option<String>,
276
277 #[serde(default)]
279 pub extension: Option<String>,
280
281 #[serde(default)]
283 pub kind: Option<String>,
284
285 #[serde(default = "default_limit")]
287 pub limit: usize,
288}
289
290#[derive(Debug, Clone, Deserialize, JsonSchema)]
292pub struct GetGitChangesInput {
293 #[serde(default)]
295 pub project: Option<String>,
296}
297
298#[derive(Debug, Clone, Serialize, JsonSchema)]
304pub struct NodeInfo {
305 pub id: u64,
307
308 pub name: String,
310
311 pub path: String,
313
314 pub kind: String,
316
317 #[serde(skip_serializing_if = "Option::is_none")]
319 pub extension: Option<String>,
320
321 #[serde(skip_serializing_if = "Option::is_none")]
323 pub language: Option<String>,
324
325 #[serde(skip_serializing_if = "HashMap::is_empty")]
327 pub metadata: HashMap<String, String>,
328}
329
330#[derive(Debug, Clone, Serialize, JsonSchema)]
332pub struct EdgeInfo {
333 pub from: String,
335
336 pub to: String,
338
339 pub relationship: String,
341}
342
343#[derive(Debug, Clone, Serialize, JsonSchema)]
345pub struct SearchNodesOutput {
346 pub nodes: Vec<NodeInfo>,
348
349 pub total_matches: usize,
351
352 pub query: String,
354}
355
356#[derive(Debug, Clone, Serialize, JsonSchema)]
358pub struct GetDependenciesOutput {
359 pub node: NodeInfo,
361
362 pub dependents: Vec<NodeInfo>,
364
365 pub dependencies: Vec<NodeInfo>,
367
368 pub edges: Vec<EdgeInfo>,
370}
371
372#[derive(Debug, Clone, Serialize, JsonSchema)]
374pub struct ImpactAnalysisOutput {
375 pub analyzed_paths: Vec<String>,
377
378 pub impacted_nodes: Vec<NodeInfo>,
380
381 pub impacted_tests: Vec<NodeInfo>,
383
384 pub impact_count: usize,
386
387 pub depth: usize,
389}
390
391#[derive(Debug, Clone, Serialize, JsonSchema)]
393pub struct GitChangesOutput {
394 pub changes: Vec<GitFileChange>,
396
397 pub change_count: usize,
399
400 pub summary: GitChangesSummary,
402}
403
404#[derive(Debug, Clone, Serialize, JsonSchema)]
406pub struct GitFileChange {
407 pub path: String,
409
410 pub kind: String,
412
413 pub staged: bool,
415}
416
417#[derive(Debug, Clone, Serialize, JsonSchema)]
419pub struct GitChangesSummary {
420 pub modified: usize,
421 pub added: usize,
422 pub deleted: usize,
423 pub untracked: usize,
424}
425
426#[derive(Debug, Clone, Serialize, JsonSchema)]
428pub struct NodeContextOutput {
429 pub node: NodeInfo,
431
432 pub neighbors: Vec<NodeInfo>,
434
435 pub edges: Vec<EdgeInfo>,
437
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub content: Option<String>,
441}
442
443#[derive(Debug, Clone, Serialize, JsonSchema)]
445pub struct ListFilesOutput {
446 pub files: Vec<NodeInfo>,
448
449 pub total: usize,
451
452 pub path: Option<String>,
454}