diff options
Diffstat (limited to 'vendor/git2/src/attr.rs')
-rw-r--r-- | vendor/git2/src/attr.rs | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/vendor/git2/src/attr.rs b/vendor/git2/src/attr.rs new file mode 100644 index 0000000..33b1d2d --- /dev/null +++ b/vendor/git2/src/attr.rs @@ -0,0 +1,175 @@ +use crate::raw; +use std::ptr; +use std::str; + +/// All possible states of an attribute. +/// +/// This enum is used to interpret the value returned by +/// [`Repository::get_attr`](crate::Repository::get_attr) and +/// [`Repository::get_attr_bytes`](crate::Repository::get_attr_bytes). +#[derive(Debug, Clone, Copy, Eq)] +pub enum AttrValue<'string> { + /// The attribute is set to true. + True, + /// The attribute is unset (set to false). + False, + /// The attribute is set to a [valid UTF-8 string](prim@str). + String(&'string str), + /// The attribute is set to a string that might not be [valid UTF-8](prim@str). + Bytes(&'string [u8]), + /// The attribute is not specified. + Unspecified, +} + +macro_rules! from_value { + ($value:expr => $string:expr) => { + match unsafe { raw::git_attr_value($value.map_or(ptr::null(), |v| v.as_ptr().cast())) } { + raw::GIT_ATTR_VALUE_TRUE => Self::True, + raw::GIT_ATTR_VALUE_FALSE => Self::False, + raw::GIT_ATTR_VALUE_STRING => $string, + raw::GIT_ATTR_VALUE_UNSPECIFIED => Self::Unspecified, + _ => unreachable!(), + } + }; +} + +impl<'string> AttrValue<'string> { + /// Returns the state of an attribute by inspecting its [value](crate::Repository::get_attr) + /// by a [string](prim@str). + /// + /// This function always returns [`AttrValue::String`] and never returns [`AttrValue::Bytes`] + /// when the attribute is set to a string. + pub fn from_string(value: Option<&'string str>) -> Self { + from_value!(value => Self::String(value.unwrap())) + } + + /// Returns the state of an attribute by inspecting its [value](crate::Repository::get_attr_bytes) + /// by a [byte](u8) [slice]. + /// + /// This function will perform UTF-8 validation when the attribute is set to a string, returns + /// [`AttrValue::String`] if it's valid UTF-8 and [`AttrValue::Bytes`] otherwise. + pub fn from_bytes(value: Option<&'string [u8]>) -> Self { + let mut value = Self::always_bytes(value); + if let Self::Bytes(bytes) = value { + if let Ok(string) = str::from_utf8(bytes) { + value = Self::String(string); + } + } + value + } + + /// Returns the state of an attribute just like [`AttrValue::from_bytes`], but skips UTF-8 + /// validation and always returns [`AttrValue::Bytes`] when it's set to a string. + pub fn always_bytes(value: Option<&'string [u8]>) -> Self { + from_value!(value => Self::Bytes(value.unwrap())) + } +} + +/// Compare two [`AttrValue`]s. +/// +/// Note that this implementation does not differentiate between [`AttrValue::String`] and +/// [`AttrValue::Bytes`]. +impl PartialEq for AttrValue<'_> { + fn eq(&self, other: &AttrValue<'_>) -> bool { + match (self, other) { + (Self::True, AttrValue::True) + | (Self::False, AttrValue::False) + | (Self::Unspecified, AttrValue::Unspecified) => true, + (AttrValue::String(string), AttrValue::Bytes(bytes)) + | (AttrValue::Bytes(bytes), AttrValue::String(string)) => string.as_bytes() == *bytes, + (AttrValue::String(left), AttrValue::String(right)) => left == right, + (AttrValue::Bytes(left), AttrValue::Bytes(right)) => left == right, + _ => false, + } + } +} + +#[cfg(test)] +mod tests { + use super::AttrValue; + + macro_rules! test_attr_value { + ($function:ident, $variant:ident) => { + const ATTR_TRUE: &str = "[internal]__TRUE__"; + const ATTR_FALSE: &str = "[internal]__FALSE__"; + const ATTR_UNSET: &str = "[internal]__UNSET__"; + let as_bytes = AsRef::<[u8]>::as_ref; + // Use `matches!` here since the `PartialEq` implementation does not differentiate + // between `String` and `Bytes`. + assert!(matches!( + AttrValue::$function(Some(ATTR_TRUE.as_ref())), + AttrValue::$variant(s) if as_bytes(s) == ATTR_TRUE.as_bytes() + )); + assert!(matches!( + AttrValue::$function(Some(ATTR_FALSE.as_ref())), + AttrValue::$variant(s) if as_bytes(s) == ATTR_FALSE.as_bytes() + )); + assert!(matches!( + AttrValue::$function(Some(ATTR_UNSET.as_ref())), + AttrValue::$variant(s) if as_bytes(s) == ATTR_UNSET.as_bytes() + )); + assert!(matches!( + AttrValue::$function(Some("foo".as_ref())), + AttrValue::$variant(s) if as_bytes(s) == b"foo" + )); + assert!(matches!( + AttrValue::$function(Some("bar".as_ref())), + AttrValue::$variant(s) if as_bytes(s) == b"bar" + )); + assert_eq!(AttrValue::$function(None), AttrValue::Unspecified); + }; + } + + #[test] + fn attr_value_from_string() { + test_attr_value!(from_string, String); + } + + #[test] + fn attr_value_from_bytes() { + test_attr_value!(from_bytes, String); + assert!(matches!( + AttrValue::from_bytes(Some(&[0xff])), + AttrValue::Bytes(&[0xff]) + )); + assert!(matches!( + AttrValue::from_bytes(Some(b"\xffoobar")), + AttrValue::Bytes(b"\xffoobar") + )); + } + + #[test] + fn attr_value_always_bytes() { + test_attr_value!(always_bytes, Bytes); + assert!(matches!( + AttrValue::always_bytes(Some(&[0xff; 2])), + AttrValue::Bytes(&[0xff, 0xff]) + )); + assert!(matches!( + AttrValue::always_bytes(Some(b"\xffoo")), + AttrValue::Bytes(b"\xffoo") + )); + } + + #[test] + fn attr_value_partial_eq() { + assert_eq!(AttrValue::True, AttrValue::True); + assert_eq!(AttrValue::False, AttrValue::False); + assert_eq!(AttrValue::String("foo"), AttrValue::String("foo")); + assert_eq!(AttrValue::Bytes(b"foo"), AttrValue::Bytes(b"foo")); + assert_eq!(AttrValue::String("bar"), AttrValue::Bytes(b"bar")); + assert_eq!(AttrValue::Bytes(b"bar"), AttrValue::String("bar")); + assert_eq!(AttrValue::Unspecified, AttrValue::Unspecified); + assert_ne!(AttrValue::True, AttrValue::False); + assert_ne!(AttrValue::False, AttrValue::Unspecified); + assert_ne!(AttrValue::Unspecified, AttrValue::True); + assert_ne!(AttrValue::True, AttrValue::String("true")); + assert_ne!(AttrValue::Unspecified, AttrValue::Bytes(b"unspecified")); + assert_ne!(AttrValue::Bytes(b"false"), AttrValue::False); + assert_ne!(AttrValue::String("unspecified"), AttrValue::Unspecified); + assert_ne!(AttrValue::String("foo"), AttrValue::String("bar")); + assert_ne!(AttrValue::Bytes(b"foo"), AttrValue::Bytes(b"bar")); + assert_ne!(AttrValue::String("foo"), AttrValue::Bytes(b"bar")); + assert_ne!(AttrValue::Bytes(b"foo"), AttrValue::String("bar")); + } +} |