git2/
describe.rs

1use std::ffi::CString;
2use std::marker;
3use std::mem;
4use std::ptr;
5
6use libc::{c_int, c_uint};
7
8use crate::util::Binding;
9use crate::{raw, Buf, Error, Repository};
10
11/// The result of a `describe` operation on either an `Describe` or a
12/// `Repository`.
13pub struct Describe<'repo> {
14    raw: *mut raw::git_describe_result,
15    _marker: marker::PhantomData<&'repo Repository>,
16}
17
18/// Options which indicate how a `Describe` is created.
19pub struct DescribeOptions {
20    raw: raw::git_describe_options,
21    pattern: CString,
22}
23
24/// Options which can be used to customize how a description is formatted.
25pub struct DescribeFormatOptions {
26    raw: raw::git_describe_format_options,
27    dirty_suffix: CString,
28}
29
30impl<'repo> Describe<'repo> {
31    /// Prints this describe result, returning the result as a string.
32    pub fn format(&self, opts: Option<&DescribeFormatOptions>) -> Result<String, Error> {
33        let buf = Buf::new();
34        let opts = opts.map(|o| &o.raw as *const _).unwrap_or(ptr::null());
35        unsafe {
36            try_call!(raw::git_describe_format(buf.raw(), self.raw, opts));
37        }
38        Ok(String::from_utf8(buf.to_vec()).unwrap())
39    }
40}
41
42impl<'repo> Binding for Describe<'repo> {
43    type Raw = *mut raw::git_describe_result;
44
45    unsafe fn from_raw(raw: *mut raw::git_describe_result) -> Describe<'repo> {
46        Describe {
47            raw,
48            _marker: marker::PhantomData,
49        }
50    }
51    fn raw(&self) -> *mut raw::git_describe_result {
52        self.raw
53    }
54}
55
56impl<'repo> Drop for Describe<'repo> {
57    fn drop(&mut self) {
58        unsafe { raw::git_describe_result_free(self.raw) }
59    }
60}
61
62impl Default for DescribeFormatOptions {
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68impl DescribeFormatOptions {
69    /// Creates a new blank set of formatting options for a description.
70    pub fn new() -> DescribeFormatOptions {
71        let mut opts = DescribeFormatOptions {
72            raw: unsafe { mem::zeroed() },
73            dirty_suffix: CString::new(Vec::new()).unwrap(),
74        };
75        opts.raw.version = 1;
76        opts.raw.abbreviated_size = 7;
77        opts
78    }
79
80    /// Sets the size of the abbreviated commit id to use.
81    ///
82    /// The value is the lower bound for the length of the abbreviated string,
83    /// and the default is 7.
84    pub fn abbreviated_size(&mut self, size: u32) -> &mut Self {
85        self.raw.abbreviated_size = size as c_uint;
86        self
87    }
88
89    /// Sets whether or not the long format is used even when a shorter name
90    /// could be used.
91    pub fn always_use_long_format(&mut self, long: bool) -> &mut Self {
92        self.raw.always_use_long_format = long as c_int;
93        self
94    }
95
96    /// If the workdir is dirty and this is set, this string will be appended to
97    /// the description string.
98    pub fn dirty_suffix(&mut self, suffix: &str) -> &mut Self {
99        self.dirty_suffix = CString::new(suffix).unwrap();
100        self.raw.dirty_suffix = self.dirty_suffix.as_ptr();
101        self
102    }
103}
104
105impl Default for DescribeOptions {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111impl DescribeOptions {
112    /// Creates a new blank set of formatting options for a description.
113    pub fn new() -> DescribeOptions {
114        let mut opts = DescribeOptions {
115            raw: unsafe { mem::zeroed() },
116            pattern: CString::new(Vec::new()).unwrap(),
117        };
118        opts.raw.version = 1;
119        opts.raw.max_candidates_tags = 10;
120        opts
121    }
122
123    #[allow(missing_docs)]
124    pub fn max_candidates_tags(&mut self, max: u32) -> &mut Self {
125        self.raw.max_candidates_tags = max as c_uint;
126        self
127    }
128
129    /// Sets the reference lookup strategy
130    ///
131    /// This behaves like the `--tags` option to git-describe.
132    pub fn describe_tags(&mut self) -> &mut Self {
133        self.raw.describe_strategy = raw::GIT_DESCRIBE_TAGS as c_uint;
134        self
135    }
136
137    /// Sets the reference lookup strategy
138    ///
139    /// This behaves like the `--all` option to git-describe.
140    pub fn describe_all(&mut self) -> &mut Self {
141        self.raw.describe_strategy = raw::GIT_DESCRIBE_ALL as c_uint;
142        self
143    }
144
145    /// Indicates when calculating the distance from the matching tag or
146    /// reference whether to only walk down the first-parent ancestry.
147    pub fn only_follow_first_parent(&mut self, follow: bool) -> &mut Self {
148        self.raw.only_follow_first_parent = follow as c_int;
149        self
150    }
151
152    /// If no matching tag or reference is found whether a describe option would
153    /// normally fail. This option indicates, however, that it will instead fall
154    /// back to showing the full id of the commit.
155    pub fn show_commit_oid_as_fallback(&mut self, show: bool) -> &mut Self {
156        self.raw.show_commit_oid_as_fallback = show as c_int;
157        self
158    }
159
160    #[allow(missing_docs)]
161    pub fn pattern(&mut self, pattern: &str) -> &mut Self {
162        self.pattern = CString::new(pattern).unwrap();
163        self.raw.pattern = self.pattern.as_ptr();
164        self
165    }
166}
167
168impl Binding for DescribeOptions {
169    type Raw = *mut raw::git_describe_options;
170
171    unsafe fn from_raw(_raw: *mut raw::git_describe_options) -> DescribeOptions {
172        panic!("unimplemened")
173    }
174    fn raw(&self) -> *mut raw::git_describe_options {
175        &self.raw as *const _ as *mut _
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use crate::DescribeOptions;
182
183    #[test]
184    fn smoke() {
185        let (_td, repo) = crate::test::repo_init();
186        let head = t!(repo.head()).target().unwrap();
187
188        let d = t!(repo.describe(DescribeOptions::new().show_commit_oid_as_fallback(true)));
189        let id = head.to_string();
190        assert_eq!(t!(d.format(None)), &id[..7]);
191
192        let obj = t!(repo.find_object(head, None));
193        let sig = t!(repo.signature());
194        t!(repo.tag("foo", &obj, &sig, "message", true));
195        let d = t!(repo.describe(&DescribeOptions::new()));
196        assert_eq!(t!(d.format(None)), "foo");
197
198        let d = t!(obj.describe(&DescribeOptions::new()));
199        assert_eq!(t!(d.format(None)), "foo");
200    }
201}