agtrace_runtime/client/
projects.rs1use crate::ops::{IndexProgress, IndexService, ProjectInfo, ProjectService};
2use agtrace_index::Database;
3use agtrace_providers::{ProviderAdapter, ScanContext};
4use agtrace_types::{discover_project_root, project_hash_from_root};
5use anyhow::Result;
6use std::path::PathBuf;
7use std::sync::{Arc, Mutex};
8
9#[derive(Debug, Clone)]
10pub struct ScanSummary {
11 pub total_sessions: usize,
12 pub scanned_files: usize,
13 pub skipped_files: usize,
14}
15
16pub struct ProjectOps {
17 db: Arc<Mutex<Database>>,
18 provider_configs: Arc<Vec<(String, PathBuf)>>,
19}
20
21impl ProjectOps {
22 pub fn new(db: Arc<Mutex<Database>>, provider_configs: Arc<Vec<(String, PathBuf)>>) -> Self {
23 Self {
24 db,
25 provider_configs,
26 }
27 }
28
29 pub fn list(&self) -> Result<Vec<ProjectInfo>> {
30 self.ensure_index_is_fresh()?;
31
32 let db = self.db.lock().unwrap();
33 let service = ProjectService::new(&db);
34 service.list_projects()
35 }
36
37 fn ensure_index_is_fresh(&self) -> Result<()> {
38 let db = self.db.lock().unwrap();
39
40 let providers: Vec<(ProviderAdapter, PathBuf)> = self
41 .provider_configs
42 .iter()
43 .filter_map(|(name, path)| {
44 agtrace_providers::create_adapter(name)
45 .ok()
46 .map(|p| (p, path.clone()))
47 })
48 .collect();
49
50 let service = IndexService::new(&db, providers);
51
52 let project_hash = discover_project_root(None)
55 .ok()
56 .map(|root| project_hash_from_root(&root.display().to_string()))
57 .unwrap_or_else(|| "unknown".to_string());
58
59 let scan_context = ScanContext {
60 project_hash,
61 project_root: None,
62 provider_filter: None,
63 };
64
65 service.run(&scan_context, false, |_progress: IndexProgress| {})?;
66
67 Ok(())
68 }
69
70 pub fn scan<F>(
71 &self,
72 scan_context: &ScanContext,
73 force: bool,
74 mut on_progress: F,
75 ) -> Result<ScanSummary>
76 where
77 F: FnMut(IndexProgress),
78 {
79 let db = self.db.lock().unwrap();
80 let providers: Vec<(ProviderAdapter, PathBuf)> = self
81 .provider_configs
82 .iter()
83 .filter_map(|(name, path)| {
84 if let Some(ref filter) = scan_context.provider_filter
86 && filter != "all"
87 && name != filter
88 {
89 return None;
90 }
91 agtrace_providers::create_adapter(name)
92 .ok()
93 .map(|p| (p, path.clone()))
94 })
95 .collect();
96 let service = IndexService::new(&db, providers);
97
98 let mut total_sessions = 0;
99 let mut scanned_files = 0;
100 let mut skipped_files = 0;
101
102 service.run(scan_context, force, |progress| {
103 if let IndexProgress::Completed {
104 total_sessions: ts,
105 scanned_files: sf,
106 skipped_files: skf,
107 } = progress
108 {
109 total_sessions = ts;
110 scanned_files = sf;
111 skipped_files = skf;
112 }
113 on_progress(progress);
114 })?;
115
116 Ok(ScanSummary {
117 total_sessions,
118 scanned_files,
119 skipped_files,
120 })
121 }
122}