agtrace_runtime/client/
projects.rs

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