1 use crate::raw; 2 use std::ptr; 3 use std::str; 4 5 /// All possible states of an attribute. 6 /// 7 /// This enum is used to interpret the value returned by 8 /// [`Repository::get_attr`](crate::Repository::get_attr) and 9 /// [`Repository::get_attr_bytes`](crate::Repository::get_attr_bytes). 10 #[derive(Debug, Clone, Copy, Eq)] 11 pub enum AttrValue<'string> { 12 /// The attribute is set to true. 13 True, 14 /// The attribute is unset (set to false). 15 False, 16 /// The attribute is set to a [valid UTF-8 string](prim@str). 17 String(&'string str), 18 /// The attribute is set to a string that might not be [valid UTF-8](prim@str). 19 Bytes(&'string [u8]), 20 /// The attribute is not specified. 21 Unspecified, 22 } 23 24 macro_rules! from_value { 25 ($value:expr => $string:expr) => { 26 match unsafe { raw::git_attr_value($value.map_or(ptr::null(), |v| v.as_ptr().cast())) } { 27 raw::GIT_ATTR_VALUE_TRUE => Self::True, 28 raw::GIT_ATTR_VALUE_FALSE => Self::False, 29 raw::GIT_ATTR_VALUE_STRING => $string, 30 raw::GIT_ATTR_VALUE_UNSPECIFIED => Self::Unspecified, 31 _ => unreachable!(), 32 } 33 }; 34 } 35 36 impl<'string> AttrValue<'string> { 37 /// Returns the state of an attribute by inspecting its [value](crate::Repository::get_attr) 38 /// by a [string](prim@str). 39 /// 40 /// This function always returns [`AttrValue::String`] and never returns [`AttrValue::Bytes`] 41 /// when the attribute is set to a string. from_string(value: Option<&'string str>) -> Self42 pub fn from_string(value: Option<&'string str>) -> Self { 43 from_value!(value => Self::String(value.unwrap())) 44 } 45 46 /// Returns the state of an attribute by inspecting its [value](crate::Repository::get_attr_bytes) 47 /// by a [byte](u8) [slice]. 48 /// 49 /// This function will perform UTF-8 validation when the attribute is set to a string, returns 50 /// [`AttrValue::String`] if it's valid UTF-8 and [`AttrValue::Bytes`] otherwise. from_bytes(value: Option<&'string [u8]>) -> Self51 pub fn from_bytes(value: Option<&'string [u8]>) -> Self { 52 let mut value = Self::always_bytes(value); 53 if let Self::Bytes(bytes) = value { 54 if let Ok(string) = str::from_utf8(bytes) { 55 value = Self::String(string); 56 } 57 } 58 value 59 } 60 61 /// Returns the state of an attribute just like [`AttrValue::from_bytes`], but skips UTF-8 62 /// validation and always returns [`AttrValue::Bytes`] when it's set to a string. always_bytes(value: Option<&'string [u8]>) -> Self63 pub fn always_bytes(value: Option<&'string [u8]>) -> Self { 64 from_value!(value => Self::Bytes(value.unwrap())) 65 } 66 } 67 68 /// Compare two [`AttrValue`]s. 69 /// 70 /// Note that this implementation does not differentiate between [`AttrValue::String`] and 71 /// [`AttrValue::Bytes`]. 72 impl PartialEq for AttrValue<'_> { eq(&self, other: &AttrValue<'_>) -> bool73 fn eq(&self, other: &AttrValue<'_>) -> bool { 74 match (self, other) { 75 (Self::True, AttrValue::True) 76 | (Self::False, AttrValue::False) 77 | (Self::Unspecified, AttrValue::Unspecified) => true, 78 (AttrValue::String(string), AttrValue::Bytes(bytes)) 79 | (AttrValue::Bytes(bytes), AttrValue::String(string)) => string.as_bytes() == *bytes, 80 (AttrValue::String(left), AttrValue::String(right)) => left == right, 81 (AttrValue::Bytes(left), AttrValue::Bytes(right)) => left == right, 82 _ => false, 83 } 84 } 85 } 86 87 #[cfg(test)] 88 mod tests { 89 use super::AttrValue; 90 91 macro_rules! test_attr_value { 92 ($function:ident, $variant:ident) => { 93 const ATTR_TRUE: &str = "[internal]__TRUE__"; 94 const ATTR_FALSE: &str = "[internal]__FALSE__"; 95 const ATTR_UNSET: &str = "[internal]__UNSET__"; 96 let as_bytes = AsRef::<[u8]>::as_ref; 97 // Use `matches!` here since the `PartialEq` implementation does not differentiate 98 // between `String` and `Bytes`. 99 assert!(matches!( 100 AttrValue::$function(Some(ATTR_TRUE.as_ref())), 101 AttrValue::$variant(s) if as_bytes(s) == ATTR_TRUE.as_bytes() 102 )); 103 assert!(matches!( 104 AttrValue::$function(Some(ATTR_FALSE.as_ref())), 105 AttrValue::$variant(s) if as_bytes(s) == ATTR_FALSE.as_bytes() 106 )); 107 assert!(matches!( 108 AttrValue::$function(Some(ATTR_UNSET.as_ref())), 109 AttrValue::$variant(s) if as_bytes(s) == ATTR_UNSET.as_bytes() 110 )); 111 assert!(matches!( 112 AttrValue::$function(Some("foo".as_ref())), 113 AttrValue::$variant(s) if as_bytes(s) == b"foo" 114 )); 115 assert!(matches!( 116 AttrValue::$function(Some("bar".as_ref())), 117 AttrValue::$variant(s) if as_bytes(s) == b"bar" 118 )); 119 assert_eq!(AttrValue::$function(None), AttrValue::Unspecified); 120 }; 121 } 122 123 #[test] attr_value_from_string()124 fn attr_value_from_string() { 125 test_attr_value!(from_string, String); 126 } 127 128 #[test] attr_value_from_bytes()129 fn attr_value_from_bytes() { 130 test_attr_value!(from_bytes, String); 131 assert!(matches!( 132 AttrValue::from_bytes(Some(&[0xff])), 133 AttrValue::Bytes(&[0xff]) 134 )); 135 assert!(matches!( 136 AttrValue::from_bytes(Some(b"\xffoobar")), 137 AttrValue::Bytes(b"\xffoobar") 138 )); 139 } 140 141 #[test] attr_value_always_bytes()142 fn attr_value_always_bytes() { 143 test_attr_value!(always_bytes, Bytes); 144 assert!(matches!( 145 AttrValue::always_bytes(Some(&[0xff; 2])), 146 AttrValue::Bytes(&[0xff, 0xff]) 147 )); 148 assert!(matches!( 149 AttrValue::always_bytes(Some(b"\xffoo")), 150 AttrValue::Bytes(b"\xffoo") 151 )); 152 } 153 154 #[test] attr_value_partial_eq()155 fn attr_value_partial_eq() { 156 assert_eq!(AttrValue::True, AttrValue::True); 157 assert_eq!(AttrValue::False, AttrValue::False); 158 assert_eq!(AttrValue::String("foo"), AttrValue::String("foo")); 159 assert_eq!(AttrValue::Bytes(b"foo"), AttrValue::Bytes(b"foo")); 160 assert_eq!(AttrValue::String("bar"), AttrValue::Bytes(b"bar")); 161 assert_eq!(AttrValue::Bytes(b"bar"), AttrValue::String("bar")); 162 assert_eq!(AttrValue::Unspecified, AttrValue::Unspecified); 163 assert_ne!(AttrValue::True, AttrValue::False); 164 assert_ne!(AttrValue::False, AttrValue::Unspecified); 165 assert_ne!(AttrValue::Unspecified, AttrValue::True); 166 assert_ne!(AttrValue::True, AttrValue::String("true")); 167 assert_ne!(AttrValue::Unspecified, AttrValue::Bytes(b"unspecified")); 168 assert_ne!(AttrValue::Bytes(b"false"), AttrValue::False); 169 assert_ne!(AttrValue::String("unspecified"), AttrValue::Unspecified); 170 assert_ne!(AttrValue::String("foo"), AttrValue::String("bar")); 171 assert_ne!(AttrValue::Bytes(b"foo"), AttrValue::Bytes(b"bar")); 172 assert_ne!(AttrValue::String("foo"), AttrValue::Bytes(b"bar")); 173 assert_ne!(AttrValue::Bytes(b"foo"), AttrValue::String("bar")); 174 } 175 } 176