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 crate::require_live_session::require_live_session(server, &req.session_id).await?;
25
26 let sid = req
27 .session_id
28 .parse::<uuid::Uuid>()
29 .map_err(|_| Status::invalid_argument("Invalid session ID"))?;
30 server.session_mgr().touch_session(&sid);
31
32 let engine = server.engine();
33
34 let ws = engine
36 .workspace_manager()
37 .get_workspace(&sid)
38 .ok_or_else(|| Status::not_found("Workspace not found for session"))?;
39
40 let (_repo_id, git_repo) = engine
42 .get_repo(&session.codebase)
43 .await
44 .map_err(|e| Status::internal(format!("Repo error: {e}")))?;
45
46 let prefix = req.prefix.as_deref().filter(|p| !p.is_empty());
49
50 let all_files = ws
51 .list_files(&git_repo, req.only_modified, prefix)
52 .map_err(|e| Status::internal(format!("List files failed: {e}")))?;
53
54 let modified_paths: std::collections::HashSet<String> =
56 ws.overlay.list_paths().into_iter().collect();
57
58 let repo_id = ws.repo_id;
60 let wm = engine.workspace_manager();
61
62 let files: Vec<FileEntry> = all_files
63 .into_iter()
64 .map(|path| {
65 let modified = modified_paths.contains(&path);
66 let modified_by_other = wm.describe_other_modifiers(&path, repo_id, sid);
67 FileEntry {
68 path,
69 modified_in_session: modified,
70 modified_by_other,
71 }
72 })
73 .collect();
74
75 info!(
76 session_id = %req.session_id,
77 file_count = files.len(),
78 only_modified = req.only_modified,
79 "FILE_LIST: served"
80 );
81
82 Ok(Response::new(FileListResponse { files }))
83}