use crate::raw;
use std::ptr;
use std::str;
#[derive(Debug, Clone, Copy, Eq)]
pub enum AttrValue<'string> {
True,
False,
String(&'string str),
Bytes(&'string [u8]),
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> {
pub fn from_string(value: Option<&'string str>) -> Self {
from_value!(value => Self::String(value.unwrap()))
}
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
}
pub fn always_bytes(value: Option<&'string [u8]>) -> Self {
from_value!(value => Self::Bytes(value.unwrap()))
}
}
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;
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"));
}
}