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
11pub struct Describe<'repo> {
14 raw: *mut raw::git_describe_result,
15 _marker: marker::PhantomData<&'repo Repository>,
16}
17
18pub struct DescribeOptions {
20 raw: raw::git_describe_options,
21 pattern: CString,
22}
23
24pub struct DescribeFormatOptions {
26 raw: raw::git_describe_format_options,
27 dirty_suffix: CString,
28}
29
30impl<'repo> Describe<'repo> {
31 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 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 pub fn abbreviated_size(&mut self, size: u32) -> &mut Self {
85 self.raw.abbreviated_size = size as c_uint;
86 self
87 }
88
89 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 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 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 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 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 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 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}