| use std::path::{Path, PathBuf}; |
| use std::sync::Arc; |
|
|
| use async_trait::async_trait; |
| use forge_app::CommandInfra; |
| use tracing::info; |
|
|
| use crate::fd::{FileDiscovery, filter_and_resolve}; |
|
|
| |
| |
| |
| |
| |
| pub struct FsGit<F> { |
| infra: Arc<F>, |
| } |
|
|
| impl<F> FsGit<F> { |
| |
| pub fn new(infra: Arc<F>) -> Self { |
| Self { infra } |
| } |
| } |
|
|
| impl<F: CommandInfra> FsGit<F> { |
| |
| |
| |
| |
| |
| |
| async fn git_ls_files(&self, dir_path: &Path) -> anyhow::Result<Vec<String>> { |
| let output = self |
| .infra |
| .execute_command( |
| "git ls-files".to_string(), |
| dir_path.to_path_buf(), |
| true, |
| None, |
| ) |
| .await?; |
|
|
| if output.exit_code != Some(0) { |
| let err = anyhow::anyhow!(output.stderr); |
| return Err(match output.exit_code { |
| Some(code) => err.context(format!("'git ls-files' exited with code {}", code)), |
| None => err, |
| }); |
| } |
|
|
| let paths = output |
| .stdout |
| .lines() |
| .filter(|line| !line.is_empty()) |
| .map(|line| line.trim().to_string()) |
| .collect(); |
|
|
| Ok(paths) |
| } |
| } |
|
|
| #[async_trait] |
| impl<F: CommandInfra + 'static> FileDiscovery for FsGit<F> { |
| async fn discover(&self, dir_path: &Path) -> anyhow::Result<Vec<PathBuf>> { |
| let paths = self.git_ls_files(dir_path).await?; |
| if paths.is_empty() { |
| return Err(anyhow::anyhow!("git ls-files returned no files")); |
| } |
| info!( |
| file_count = paths.len(), |
| "Discovered files via git ls-files" |
| ); |
| filter_and_resolve(dir_path, paths) |
| } |
| } |
|
|