Skip to main content

git_spawn/command/
ls_files.rs

1//! `git ls-files` — show information about files in the index and working tree.
2//!
3//! ```no_run
4//! use git_spawn::{GitCommand, LsFilesCommand};
5//!
6//! # async fn example() -> git_spawn::Result<()> {
7//! let mut cmd = LsFilesCommand::new();
8//! cmd.current_dir("/repo").cached();
9//! let out = cmd.execute().await?;
10//! for path in out.stdout.lines() {
11//!     println!("{path}");
12//! }
13//! # Ok(())
14//! # }
15//! ```
16
17use crate::command::{CommandExecutor, CommandOutput, GitCommand};
18use crate::error::Result;
19use async_trait::async_trait;
20
21/// Builder for `git ls-files`.
22#[derive(Debug, Clone, Default)]
23pub struct LsFilesCommand {
24    /// Shared executor.
25    pub executor: CommandExecutor,
26    /// `--cached` / `-c`.
27    pub cached: bool,
28    /// `--deleted` / `-d`.
29    pub deleted: bool,
30    /// `--modified` / `-m`.
31    pub modified: bool,
32    /// `--others` / `-o`.
33    pub others: bool,
34    /// `--ignored` / `-i`.
35    pub ignored: bool,
36    /// `--stage` / `-s`.
37    pub stage: bool,
38    /// `--unmerged` / `-u`.
39    pub unmerged: bool,
40    /// `--exclude-standard`.
41    pub exclude_standard: bool,
42    /// `-z` NUL-terminate.
43    pub null_terminate: bool,
44    /// Pathspec filters.
45    pub paths: Vec<String>,
46}
47
48impl LsFilesCommand {
49    /// New command.
50    #[must_use]
51    pub fn new() -> Self {
52        Self::default()
53    }
54
55    /// Show cached files (default behavior).
56    pub fn cached(&mut self) -> &mut Self {
57        self.cached = true;
58        self
59    }
60
61    /// Show deleted files.
62    pub fn deleted(&mut self) -> &mut Self {
63        self.deleted = true;
64        self
65    }
66
67    /// Show modified files.
68    pub fn modified(&mut self) -> &mut Self {
69        self.modified = true;
70        self
71    }
72
73    /// Show untracked (other) files.
74    pub fn others(&mut self) -> &mut Self {
75        self.others = true;
76        self
77    }
78
79    /// Show ignored files (combine with `--others`).
80    pub fn ignored(&mut self) -> &mut Self {
81        self.ignored = true;
82        self
83    }
84
85    /// Include stage numbers and SHAs.
86    pub fn stage(&mut self) -> &mut Self {
87        self.stage = true;
88        self
89    }
90
91    /// Show unmerged files only.
92    pub fn unmerged(&mut self) -> &mut Self {
93        self.unmerged = true;
94        self
95    }
96
97    /// Honor `.gitignore` when listing others.
98    pub fn exclude_standard(&mut self) -> &mut Self {
99        self.exclude_standard = true;
100        self
101    }
102
103    /// NUL-terminate output (useful for paths with newlines).
104    pub fn null_terminate(&mut self) -> &mut Self {
105        self.null_terminate = true;
106        self
107    }
108
109    /// Filter by path.
110    pub fn path(&mut self, p: impl Into<String>) -> &mut Self {
111        self.paths.push(p.into());
112        self
113    }
114}
115
116#[async_trait]
117impl GitCommand for LsFilesCommand {
118    type Output = CommandOutput;
119    fn get_executor(&self) -> &CommandExecutor {
120        &self.executor
121    }
122    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
123        &mut self.executor
124    }
125    fn build_command_args(&self) -> Vec<String> {
126        let mut args = vec!["ls-files".to_string()];
127        if self.cached {
128            args.push("--cached".into());
129        }
130        if self.deleted {
131            args.push("--deleted".into());
132        }
133        if self.modified {
134            args.push("--modified".into());
135        }
136        if self.others {
137            args.push("--others".into());
138        }
139        if self.ignored {
140            args.push("--ignored".into());
141        }
142        if self.stage {
143            args.push("--stage".into());
144        }
145        if self.unmerged {
146            args.push("--unmerged".into());
147        }
148        if self.exclude_standard {
149            args.push("--exclude-standard".into());
150        }
151        if self.null_terminate {
152            args.push("-z".into());
153        }
154        if !self.paths.is_empty() {
155            args.push("--".into());
156            args.extend(self.paths.iter().cloned());
157        }
158        args
159    }
160    async fn execute(&self) -> Result<CommandOutput> {
161        self.execute_raw().await
162    }
163}