Skip to main content

git_spawn/command/
rev_parse.rs

1//! `git rev-parse` — pick out and massage parameters.
2//!
3//! `rev-parse` is the swiss army knife of git plumbing: resolve refs to SHAs,
4//! query the `.git` directory, show the top-level, check whether the cwd is
5//! inside a working tree, etc. This wrapper exposes the common modes and
6//! returns stdout trimmed as [`String`] so callers can parse as needed.
7//!
8//! ```no_run
9//! use git_spawn::{GitCommand, RevParseCommand};
10//!
11//! # async fn example() -> git_spawn::Result<()> {
12//! let mut cmd = RevParseCommand::new();
13//! cmd.arg_str("HEAD").current_dir("/some/repo");
14//! let sha = cmd.execute().await?;
15//! println!("HEAD -> {sha}");
16//! # Ok(())
17//! # }
18//! ```
19
20use crate::command::{CommandExecutor, GitCommand};
21use crate::error::Result;
22use async_trait::async_trait;
23
24/// Builder for `git rev-parse`.
25#[derive(Debug, Clone, Default)]
26pub struct RevParseCommand {
27    /// Shared executor.
28    pub executor: CommandExecutor,
29    /// Arguments / refs / flags to pass to `rev-parse`.
30    pub rev_args: Vec<String>,
31    /// `--verify`.
32    pub verify: bool,
33    /// `--abbrev-ref`.
34    pub abbrev_ref: bool,
35    /// `--short[=N]`.
36    pub short: Option<Option<u32>>,
37    /// `--show-toplevel`.
38    pub show_toplevel: bool,
39    /// `--git-dir`.
40    pub git_dir: bool,
41    /// `--is-inside-work-tree`.
42    pub is_inside_work_tree: bool,
43    /// `--is-bare-repository`.
44    pub is_bare_repository: bool,
45    /// `--absolute-git-dir`.
46    pub absolute_git_dir: bool,
47}
48
49impl RevParseCommand {
50    /// New empty `rev-parse` command.
51    #[must_use]
52    pub fn new() -> Self {
53        Self::default()
54    }
55
56    /// Add a ref/rev string (e.g. `"HEAD"`, `"main"`, `"HEAD~3"`).
57    pub fn arg_str(&mut self, s: impl Into<String>) -> &mut Self {
58        self.rev_args.push(s.into());
59        self
60    }
61
62    /// `--verify`: error if the argument is not a valid object.
63    pub fn verify(&mut self) -> &mut Self {
64        self.verify = true;
65        self
66    }
67
68    /// `--abbrev-ref`: print the short ref name.
69    pub fn abbrev_ref(&mut self) -> &mut Self {
70        self.abbrev_ref = true;
71        self
72    }
73
74    /// `--short` with default length.
75    pub fn short(&mut self) -> &mut Self {
76        self.short = Some(None);
77        self
78    }
79
80    /// `--short=N`.
81    pub fn short_len(&mut self, n: u32) -> &mut Self {
82        self.short = Some(Some(n));
83        self
84    }
85
86    /// `--show-toplevel`.
87    pub fn show_toplevel(&mut self) -> &mut Self {
88        self.show_toplevel = true;
89        self
90    }
91
92    /// `--git-dir`.
93    pub fn git_dir(&mut self) -> &mut Self {
94        self.git_dir = true;
95        self
96    }
97
98    /// `--absolute-git-dir`.
99    pub fn absolute_git_dir(&mut self) -> &mut Self {
100        self.absolute_git_dir = true;
101        self
102    }
103
104    /// `--is-inside-work-tree`.
105    pub fn is_inside_work_tree(&mut self) -> &mut Self {
106        self.is_inside_work_tree = true;
107        self
108    }
109
110    /// `--is-bare-repository`.
111    pub fn is_bare_repository(&mut self) -> &mut Self {
112        self.is_bare_repository = true;
113        self
114    }
115}
116
117#[async_trait]
118impl GitCommand for RevParseCommand {
119    /// Trimmed stdout — typically a SHA, path, or `true`/`false` string.
120    type Output = String;
121
122    fn get_executor(&self) -> &CommandExecutor {
123        &self.executor
124    }
125
126    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
127        &mut self.executor
128    }
129
130    fn build_command_args(&self) -> Vec<String> {
131        let mut args = vec!["rev-parse".to_string()];
132        if self.verify {
133            args.push("--verify".into());
134        }
135        if self.abbrev_ref {
136            args.push("--abbrev-ref".into());
137        }
138        match self.short {
139            Some(None) => args.push("--short".into()),
140            Some(Some(n)) => args.push(format!("--short={n}")),
141            None => {}
142        }
143        if self.show_toplevel {
144            args.push("--show-toplevel".into());
145        }
146        if self.git_dir {
147            args.push("--git-dir".into());
148        }
149        if self.absolute_git_dir {
150            args.push("--absolute-git-dir".into());
151        }
152        if self.is_inside_work_tree {
153            args.push("--is-inside-work-tree".into());
154        }
155        if self.is_bare_repository {
156            args.push("--is-bare-repository".into());
157        }
158        args.extend(self.rev_args.iter().cloned());
159        args
160    }
161
162    async fn execute(&self) -> Result<String> {
163        let out = self.execute_raw().await?;
164        Ok(out.stdout_trimmed().to_string())
165    }
166}