git_proc/
rev_parse.rs

1use std::path::Path;
2
3use crate::CommandError;
4
5/// Create a new `git rev-parse` command builder.
6#[must_use]
7pub fn new() -> RevParse<'static> {
8    RevParse::new()
9}
10
11/// Builder for `git rev-parse` command.
12///
13/// See `git rev-parse --help` for full documentation.
14#[derive(Debug)]
15pub struct RevParse<'a> {
16    repo_path: Option<&'a Path>,
17    abbrev_ref: bool,
18    symbolic_full_name: bool,
19    rev: Option<&'a str>,
20}
21
22impl<'a> RevParse<'a> {
23    #[must_use]
24    fn new() -> Self {
25        Self {
26            repo_path: None,
27            abbrev_ref: false,
28            symbolic_full_name: false,
29            rev: None,
30        }
31    }
32
33    /// Set the repository path (`-C <path>`).
34    #[must_use]
35    pub fn repo_path(mut self, path: &'a Path) -> Self {
36        self.repo_path = Some(path);
37        self
38    }
39
40    crate::flag_methods! {
41        /// Output short ref name (e.g., `main` instead of `refs/heads/main`).
42        ///
43        /// Corresponds to `--abbrev-ref`.
44        pub fn abbrev_ref / abbrev_ref_if, abbrev_ref, "Conditionally output short ref name."
45    }
46
47    crate::flag_methods! {
48        /// Output full symbolic ref name.
49        ///
50        /// Corresponds to `--symbolic-full-name`.
51        pub fn symbolic_full_name / symbolic_full_name_if, symbolic_full_name, "Conditionally output full symbolic ref name."
52    }
53
54    /// Set the revision to parse (e.g., `HEAD`, `@{u}`).
55    #[must_use]
56    pub fn rev(mut self, rev: &'a str) -> Self {
57        self.rev = Some(rev);
58        self
59    }
60
61    /// Capture stdout from this command.
62    #[must_use]
63    pub fn stdout(self) -> cmd_proc::Capture {
64        self.build().stdout()
65    }
66
67    /// Execute and return full output regardless of exit status.
68    ///
69    /// Use this when you need to inspect stderr on failure.
70    pub fn output(self) -> Result<cmd_proc::Output, CommandError> {
71        self.build().output()
72    }
73
74    fn build(self) -> cmd_proc::Command {
75        crate::base_command(self.repo_path)
76            .argument("rev-parse")
77            .optional_argument(self.abbrev_ref.then_some("--abbrev-ref"))
78            .optional_argument(self.symbolic_full_name.then_some("--symbolic-full-name"))
79            .optional_argument(self.rev)
80    }
81}
82
83impl Default for RevParse<'_> {
84    fn default() -> Self {
85        Self::new()
86    }
87}
88
89#[cfg(feature = "test-utils")]
90impl RevParse<'_> {
91    /// Compare the built command with another command using debug representation.
92    ///
93    /// This is useful for testing command construction without executing.
94    pub fn test_eq(&self, other: &cmd_proc::Command) {
95        // Clone self to build without consuming
96        let command = Self {
97            repo_path: self.repo_path,
98            abbrev_ref: self.abbrev_ref,
99            symbolic_full_name: self.symbolic_full_name,
100            rev: self.rev,
101        }
102        .build();
103        command.test_eq(other);
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_rev_parse_head() {
113        let output = RevParse::new().rev("HEAD").stdout().string().unwrap();
114        assert!(!output.trim().is_empty());
115    }
116
117    #[test]
118    fn test_rev_parse_abbrev_ref() {
119        let output = RevParse::new()
120            .abbrev_ref()
121            .rev("HEAD")
122            .stdout()
123            .string()
124            .unwrap();
125        assert!(!output.trim().is_empty());
126    }
127}