agtrace_providers/
legacy.rs

1use anyhow::Result;
2use std::path::{Path, PathBuf};
3
4use crate::traits::ProviderAdapter;
5
6#[derive(Debug, Clone)]
7pub struct LogFileMetadata {
8    pub path: String,
9    pub role: String,
10    pub file_size: Option<i64>,
11    pub mod_time: Option<String>,
12}
13
14#[derive(Debug, Clone)]
15pub struct SessionMetadata {
16    pub session_id: String,
17    pub project_hash: String,
18    pub project_root: Option<String>,
19    pub provider: String,
20    pub start_ts: Option<String>,
21    pub end_ts: Option<String>,
22    pub snippet: Option<String>,
23    pub log_files: Vec<LogFileMetadata>,
24}
25
26pub struct ScanContext {
27    pub project_hash: String,
28    pub project_root: Option<String>,
29    pub provider_filter: Option<String>,
30}
31
32impl ProviderAdapter {
33    /// Legacy support: Scan sessions and convert to full SessionMetadata
34    ///
35    /// This bridges the gap between the new efficient SessionIndex and the old UI-heavy SessionMetadata.
36    /// The new discovery layer provides lightweight SessionIndex, and this method enriches it with
37    /// file metadata for backward compatibility with existing UI code.
38    pub fn scan_legacy(
39        &self,
40        log_root: &Path,
41        context: &ScanContext,
42    ) -> Result<Vec<SessionMetadata>> {
43        let sessions = self.discovery.scan_sessions(log_root)?;
44
45        let mut metadata_list = Vec::new();
46
47        for session in sessions {
48            if let Some(expected_root) = &context.project_root {
49                if let Some(session_root) = &session.project_root {
50                    let session_normalized = session_root.trim_end_matches('/');
51                    let expected_normalized = expected_root.trim_end_matches('/');
52                    if session_normalized != expected_normalized {
53                        continue;
54                    }
55                } else if self.id() != "gemini" {
56                    continue;
57                }
58            }
59
60            let mut log_files = Vec::new();
61
62            let to_log_file = |path: &PathBuf, role: &str| -> LogFileMetadata {
63                let meta = std::fs::metadata(path).ok();
64                LogFileMetadata {
65                    path: path.display().to_string(),
66                    role: role.to_string(),
67                    file_size: meta.as_ref().map(|m| m.len() as i64),
68                    mod_time: meta
69                        .and_then(|m| m.modified().ok())
70                        .map(|t| format!("{:?}", t)),
71                }
72            };
73
74            log_files.push(to_log_file(&session.main_file, "main"));
75
76            for side_file in &session.sidechain_files {
77                log_files.push(to_log_file(side_file, "sidechain"));
78            }
79
80            let project_hash = if let Some(ref root) = session.project_root {
81                agtrace_types::project_hash_from_root(root)
82            } else if self.id() == "gemini" {
83                // For Gemini, extract project_hash directly from the file
84                use crate::gemini::io::extract_project_hash_from_gemini_file;
85                extract_project_hash_from_gemini_file(&session.main_file)
86                    .unwrap_or_else(|| context.project_hash.clone())
87            } else {
88                context.project_hash.clone()
89            };
90
91            metadata_list.push(SessionMetadata {
92                session_id: session.session_id,
93                project_hash,
94                project_root: session.project_root,
95                provider: self.id().to_string(),
96                start_ts: session.timestamp,
97                end_ts: None,
98                snippet: session.snippet,
99                log_files,
100            });
101        }
102
103        Ok(metadata_list)
104    }
105}