Skip to main content

git_spawn/command/
log.rs

1//! `git log` — show commit logs.
2
3use crate::command::{CommandExecutor, CommandOutput, GitCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Builder for `git log`.
8#[derive(Debug, Clone, Default)]
9pub struct LogCommand {
10    /// Shared executor.
11    pub executor: CommandExecutor,
12    /// `-n`.
13    pub max_count: Option<u32>,
14    /// `--skip`.
15    pub skip: Option<u32>,
16    /// `--oneline`.
17    pub oneline: bool,
18    /// `--graph`.
19    pub graph: bool,
20    /// `--all`.
21    pub all: bool,
22    /// `--reverse`.
23    pub reverse: bool,
24    /// `--format` / `--pretty=format:`.
25    pub format: Option<String>,
26    /// `--since`.
27    pub since: Option<String>,
28    /// `--until`.
29    pub until: Option<String>,
30    /// `--author`.
31    pub author: Option<String>,
32    /// `--grep`.
33    pub grep: Option<String>,
34    /// Revision range (e.g. `HEAD~5..HEAD`) or refs.
35    pub revisions: Vec<String>,
36    /// Pathspec filters.
37    pub paths: Vec<String>,
38}
39
40impl LogCommand {
41    /// New `log` builder.
42    #[must_use]
43    pub fn new() -> Self {
44        Self::default()
45    }
46
47    /// Limit to `n` commits.
48    pub fn max_count(&mut self, n: u32) -> &mut Self {
49        self.max_count = Some(n);
50        self
51    }
52
53    /// Skip first `n` commits.
54    pub fn skip(&mut self, n: u32) -> &mut Self {
55        self.skip = Some(n);
56        self
57    }
58
59    /// `--oneline`.
60    pub fn oneline(&mut self) -> &mut Self {
61        self.oneline = true;
62        self
63    }
64
65    /// `--graph`.
66    pub fn graph(&mut self) -> &mut Self {
67        self.graph = true;
68        self
69    }
70
71    /// `--all`.
72    pub fn all(&mut self) -> &mut Self {
73        self.all = true;
74        self
75    }
76
77    /// `--reverse`.
78    pub fn reverse(&mut self) -> &mut Self {
79        self.reverse = true;
80        self
81    }
82
83    /// Set a `--format=...` template.
84    pub fn format(&mut self, fmt: impl Into<String>) -> &mut Self {
85        self.format = Some(fmt.into());
86        self
87    }
88
89    /// Filter by `--since`.
90    pub fn since(&mut self, s: impl Into<String>) -> &mut Self {
91        self.since = Some(s.into());
92        self
93    }
94
95    /// Filter by `--until`.
96    pub fn until(&mut self, s: impl Into<String>) -> &mut Self {
97        self.until = Some(s.into());
98        self
99    }
100
101    /// Filter by author.
102    pub fn author(&mut self, s: impl Into<String>) -> &mut Self {
103        self.author = Some(s.into());
104        self
105    }
106
107    /// Filter by commit-message grep.
108    pub fn grep(&mut self, s: impl Into<String>) -> &mut Self {
109        self.grep = Some(s.into());
110        self
111    }
112
113    /// Add a revision/range/ref.
114    pub fn revision(&mut self, r: impl Into<String>) -> &mut Self {
115        self.revisions.push(r.into());
116        self
117    }
118
119    /// Filter by path.
120    pub fn path(&mut self, p: impl Into<String>) -> &mut Self {
121        self.paths.push(p.into());
122        self
123    }
124}
125
126#[async_trait]
127impl GitCommand for LogCommand {
128    type Output = CommandOutput;
129
130    fn get_executor(&self) -> &CommandExecutor {
131        &self.executor
132    }
133
134    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
135        &mut self.executor
136    }
137
138    fn build_command_args(&self) -> Vec<String> {
139        let mut args = vec!["log".to_string()];
140        if let Some(n) = self.max_count {
141            args.push(format!("-n{n}"));
142        }
143        if let Some(n) = self.skip {
144            args.push(format!("--skip={n}"));
145        }
146        if self.oneline {
147            args.push("--oneline".into());
148        }
149        if self.graph {
150            args.push("--graph".into());
151        }
152        if self.all {
153            args.push("--all".into());
154        }
155        if self.reverse {
156            args.push("--reverse".into());
157        }
158        if let Some(f) = &self.format {
159            args.push(format!("--format={f}"));
160        }
161        if let Some(s) = &self.since {
162            args.push(format!("--since={s}"));
163        }
164        if let Some(s) = &self.until {
165            args.push(format!("--until={s}"));
166        }
167        if let Some(s) = &self.author {
168            args.push(format!("--author={s}"));
169        }
170        if let Some(s) = &self.grep {
171            args.push(format!("--grep={s}"));
172        }
173        args.extend(self.revisions.iter().cloned());
174        if !self.paths.is_empty() {
175            args.push("--".into());
176            args.extend(self.paths.iter().cloned());
177        }
178        args
179    }
180
181    async fn execute(&self) -> Result<CommandOutput> {
182        self.execute_raw().await
183    }
184}