1 use std::ffi::CString; 2 use std::marker; 3 use std::mem; 4 use std::ptr; 5 6 use libc::{c_int, c_uint}; 7 8 use crate::util::Binding; 9 use crate::{raw, Buf, Error, Repository}; 10 11 /// The result of a `describe` operation on either an `Describe` or a 12 /// `Repository`. 13 pub 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. 19 pub 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. 25 pub struct DescribeFormatOptions { 26 raw: raw::git_describe_format_options, 27 dirty_suffix: CString, 28 } 29 30 impl<'repo> Describe<'repo> { 31 /// Prints this describe result, returning the result as a string. format(&self, opts: Option<&DescribeFormatOptions>) -> Result<String, Error>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 42 impl<'repo> Binding for Describe<'repo> { 43 type Raw = *mut raw::git_describe_result; 44 from_raw(raw: *mut raw::git_describe_result) -> Describe<'repo>45 unsafe fn from_raw(raw: *mut raw::git_describe_result) -> Describe<'repo> { 46 Describe { 47 raw, 48 _marker: marker::PhantomData, 49 } 50 } raw(&self) -> *mut raw::git_describe_result51 fn raw(&self) -> *mut raw::git_describe_result { 52 self.raw 53 } 54 } 55 56 impl<'repo> Drop for Describe<'repo> { drop(&mut self)57 fn drop(&mut self) { 58 unsafe { raw::git_describe_result_free(self.raw) } 59 } 60 } 61 62 impl Default for DescribeFormatOptions { default() -> Self63 fn default() -> Self { 64 Self::new() 65 } 66 } 67 68 impl DescribeFormatOptions { 69 /// Creates a new blank set of formatting options for a description. new() -> DescribeFormatOptions70 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. abbreviated_size(&mut self, size: u32) -> &mut Self84 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. always_use_long_format(&mut self, long: bool) -> &mut Self91 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. dirty_suffix(&mut self, suffix: &str) -> &mut Self98 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 105 impl Default for DescribeOptions { default() -> Self106 fn default() -> Self { 107 Self::new() 108 } 109 } 110 111 impl DescribeOptions { 112 /// Creates a new blank set of formatting options for a description. new() -> DescribeOptions113 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)] max_candidates_tags(&mut self, max: u32) -> &mut Self124 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. describe_tags(&mut self) -> &mut Self132 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. describe_all(&mut self) -> &mut Self140 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. only_follow_first_parent(&mut self, follow: bool) -> &mut Self147 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. show_commit_oid_as_fallback(&mut self, show: bool) -> &mut Self155 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)] pattern(&mut self, pattern: &str) -> &mut Self161 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 168 impl Binding for DescribeOptions { 169 type Raw = *mut raw::git_describe_options; 170 from_raw(_raw: *mut raw::git_describe_options) -> DescribeOptions171 unsafe fn from_raw(_raw: *mut raw::git_describe_options) -> DescribeOptions { 172 panic!("unimplemened") 173 } raw(&self) -> *mut raw::git_describe_options174 fn raw(&self) -> *mut raw::git_describe_options { 175 &self.raw as *const _ as *mut _ 176 } 177 } 178 179 #[cfg(test)] 180 mod tests { 181 use crate::DescribeOptions; 182 183 #[test] smoke()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 } 202