1use tonic::{Response, Status};
2use tracing::info;
3
4use crate::server::ProtocolServer;
5use crate::validation::validate_file_path;
6use crate::{FileEntry, FileListRequest, FileListResponse};
7
8pub async fn handle_file_list(
13 server: &ProtocolServer,
14 req: FileListRequest,
15) -> Result<Response<FileListResponse>, Status> {
16 if let Some(ref prefix) = req.prefix {
18 if !prefix.is_empty() {
19 validate_file_path(prefix)?;
20 }
21 }
22
23 let session = server.validate_session(&req.session_id)?;
24
25 let sid = req
26 .session_id
27 .parse::<uuid::Uuid>()
28 .map_err(|_| Status::invalid_argument("Invalid session ID"))?;
29 server.session_mgr().touch_session(&sid);
30
31 let engine = server.engine();
32
33 let ws = engine
35 .workspace_manager()
36 .get_workspace(&sid)
37 .ok_or_else(|| Status::not_found("Workspace not found for session"))?;
38
39 let (_repo_id, git_repo) = engine
41 .get_repo(&session.codebase)
42 .await
43 .map_err(|e| Status::internal(format!("Repo error: {e}")))?;
44
45 let prefix = req.prefix.as_deref().filter(|p| !p.is_empty());
48
49 let all_files = ws
50 .list_files(&git_repo, req.only_modified, prefix)
51 .map_err(|e| Status::internal(format!("List files failed: {e}")))?;
52
53 let modified_paths: std::collections::HashSet<String> =
55 ws.overlay.list_paths().into_iter().collect();
56
57 let repo_id = ws.repo_id;
59 let wm = engine.workspace_manager();
60
61 let files: Vec<FileEntry> = all_files
62 .into_iter()
63 .map(|path| {
64 let modified = modified_paths.contains(&path);
65 let modified_by_other = wm.describe_other_modifiers(&path, repo_id, sid);
66 FileEntry {
67 path,
68 modified_in_session: modified,
69 modified_by_other,
70 }
71 })
72 .collect();
73
74 info!(
75 session_id = %req.session_id,
76 file_count = files.len(),
77 only_modified = req.only_modified,
78 "FILE_LIST: served"
79 );
80
81 Ok(Response::new(FileListResponse { files }))
82}