| use std::ffi::CString; |
| use std::marker; |
| use std::mem; |
| use std::ptr; |
| |
| use libc::{c_int, c_uint}; |
| |
| use crate::util::Binding; |
| use crate::{raw, Buf, Error, Repository}; |
| |
| /// The result of a `describe` operation on either an `Describe` or a |
| /// `Repository`. |
| pub struct Describe<'repo> { |
| raw: *mut raw::git_describe_result, |
| _marker: marker::PhantomData<&'repo Repository>, |
| } |
| |
| /// Options which indicate how a `Describe` is created. |
| pub struct DescribeOptions { |
| raw: raw::git_describe_options, |
| pattern: CString, |
| } |
| |
| /// Options which can be used to customize how a description is formatted. |
| pub struct DescribeFormatOptions { |
| raw: raw::git_describe_format_options, |
| dirty_suffix: CString, |
| } |
| |
| impl<'repo> Describe<'repo> { |
| /// Prints this describe result, returning the result as a string. |
| pub fn format(&self, opts: Option<&DescribeFormatOptions>) -> Result<String, Error> { |
| let buf = Buf::new(); |
| let opts = opts.map(|o| &o.raw as *const _).unwrap_or(ptr::null()); |
| unsafe { |
| try_call!(raw::git_describe_format(buf.raw(), self.raw, opts)); |
| } |
| Ok(String::from_utf8(buf.to_vec()).unwrap()) |
| } |
| } |
| |
| impl<'repo> Binding for Describe<'repo> { |
| type Raw = *mut raw::git_describe_result; |
| |
| unsafe fn from_raw(raw: *mut raw::git_describe_result) -> Describe<'repo> { |
| Describe { |
| raw, |
| _marker: marker::PhantomData, |
| } |
| } |
| fn raw(&self) -> *mut raw::git_describe_result { |
| self.raw |
| } |
| } |
| |
| impl<'repo> Drop for Describe<'repo> { |
| fn drop(&mut self) { |
| unsafe { raw::git_describe_result_free(self.raw) } |
| } |
| } |
| |
| impl Default for DescribeFormatOptions { |
| fn default() -> Self { |
| Self::new() |
| } |
| } |
| |
| impl DescribeFormatOptions { |
| /// Creates a new blank set of formatting options for a description. |
| pub fn new() -> DescribeFormatOptions { |
| let mut opts = DescribeFormatOptions { |
| raw: unsafe { mem::zeroed() }, |
| dirty_suffix: CString::new(Vec::new()).unwrap(), |
| }; |
| opts.raw.version = 1; |
| opts.raw.abbreviated_size = 7; |
| opts |
| } |
| |
| /// Sets the size of the abbreviated commit id to use. |
| /// |
| /// The value is the lower bound for the length of the abbreviated string, |
| /// and the default is 7. |
| pub fn abbreviated_size(&mut self, size: u32) -> &mut Self { |
| self.raw.abbreviated_size = size as c_uint; |
| self |
| } |
| |
| /// Sets whether or not the long format is used even when a shorter name |
| /// could be used. |
| pub fn always_use_long_format(&mut self, long: bool) -> &mut Self { |
| self.raw.always_use_long_format = long as c_int; |
| self |
| } |
| |
| /// If the workdir is dirty and this is set, this string will be appended to |
| /// the description string. |
| pub fn dirty_suffix(&mut self, suffix: &str) -> &mut Self { |
| self.dirty_suffix = CString::new(suffix).unwrap(); |
| self.raw.dirty_suffix = self.dirty_suffix.as_ptr(); |
| self |
| } |
| } |
| |
| impl Default for DescribeOptions { |
| fn default() -> Self { |
| Self::new() |
| } |
| } |
| |
| impl DescribeOptions { |
| /// Creates a new blank set of formatting options for a description. |
| pub fn new() -> DescribeOptions { |
| let mut opts = DescribeOptions { |
| raw: unsafe { mem::zeroed() }, |
| pattern: CString::new(Vec::new()).unwrap(), |
| }; |
| opts.raw.version = 1; |
| opts.raw.max_candidates_tags = 10; |
| opts |
| } |
| |
| #[allow(missing_docs)] |
| pub fn max_candidates_tags(&mut self, max: u32) -> &mut Self { |
| self.raw.max_candidates_tags = max as c_uint; |
| self |
| } |
| |
| /// Sets the reference lookup strategy |
| /// |
| /// This behaves like the `--tags` option to git-describe. |
| pub fn describe_tags(&mut self) -> &mut Self { |
| self.raw.describe_strategy = raw::GIT_DESCRIBE_TAGS as c_uint; |
| self |
| } |
| |
| /// Sets the reference lookup strategy |
| /// |
| /// This behaves like the `--all` option to git-describe. |
| pub fn describe_all(&mut self) -> &mut Self { |
| self.raw.describe_strategy = raw::GIT_DESCRIBE_ALL as c_uint; |
| self |
| } |
| |
| /// Indicates when calculating the distance from the matching tag or |
| /// reference whether to only walk down the first-parent ancestry. |
| pub fn only_follow_first_parent(&mut self, follow: bool) -> &mut Self { |
| self.raw.only_follow_first_parent = follow as c_int; |
| self |
| } |
| |
| /// If no matching tag or reference is found whether a describe option would |
| /// normally fail. This option indicates, however, that it will instead fall |
| /// back to showing the full id of the commit. |
| pub fn show_commit_oid_as_fallback(&mut self, show: bool) -> &mut Self { |
| self.raw.show_commit_oid_as_fallback = show as c_int; |
| self |
| } |
| |
| #[allow(missing_docs)] |
| pub fn pattern(&mut self, pattern: &str) -> &mut Self { |
| self.pattern = CString::new(pattern).unwrap(); |
| self.raw.pattern = self.pattern.as_ptr(); |
| self |
| } |
| } |
| |
| impl Binding for DescribeOptions { |
| type Raw = *mut raw::git_describe_options; |
| |
| unsafe fn from_raw(_raw: *mut raw::git_describe_options) -> DescribeOptions { |
| panic!("unimplemened") |
| } |
| fn raw(&self) -> *mut raw::git_describe_options { |
| &self.raw as *const _ as *mut _ |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use crate::DescribeOptions; |
| |
| #[test] |
| fn smoke() { |
| let (_td, repo) = crate::test::repo_init(); |
| let head = t!(repo.head()).target().unwrap(); |
| |
| let d = t!(repo.describe(DescribeOptions::new().show_commit_oid_as_fallback(true))); |
| let id = head.to_string(); |
| assert_eq!(t!(d.format(None)), &id[..7]); |
| |
| let obj = t!(repo.find_object(head, None)); |
| let sig = t!(repo.signature()); |
| t!(repo.tag("foo", &obj, &sig, "message", true)); |
| let d = t!(repo.describe(&DescribeOptions::new())); |
| assert_eq!(t!(d.format(None)), "foo"); |
| |
| let d = t!(obj.describe(&DescribeOptions::new())); |
| assert_eq!(t!(d.format(None)), "foo"); |
| } |
| } |