//! Rust containers for valid `git-config` types. #[cfg(feature = "serde")]use serde::{Serialize, Serializer};use std::borrow::Cow;use std::convert::TryFrom;use std::fmt::Display;use std::str::FromStr; /// Removes quotes, if any, from the provided inputs. This assumes the input/// contains a even number of unescaped quotes, and will unescape escaped/// quotes. The return values should be safe for value interpretation.////// This has optimizations for fully-quoted values, where the returned value/// will be a borrowed reference if the only mutation necessary is to unquote/// the value.////// This is the function used to normalize raw values from higher level/// abstractions over the [`parser`] implementation. Generally speaking these/// high level abstractions will handle normalization for you, and you do not/// need to call this yourself. However, if you're directly handling events/// from the parser, you may want to use this to help with value interpretation.////// Generally speaking, you'll want to use one of the variants of this function,/// such as [`normalize_str`] or [`normalize_vec`].////// # Examples////// Values don't need modification are returned borrowed, without allocation.////// ```/// # use std::borrow::Cow;/// # use git_config::values::normalize_str;/// assert_eq!(normalize_str("hello world"), Cow::Borrowed(b"hello world".into()));/// ```////// Fully quoted values are optimized to not need allocations.////// ```/// # use std::borrow::Cow;/// # use git_config::values::normalize_str;/// assert_eq!(normalize_str("\"hello world\""), Cow::Borrowed(b"hello world".into()));/// ```////// Quoted values are unwrapped as an owned variant.////// ```/// # use std::borrow::Cow;/// # use git_config::values::normalize_str;/// assert_eq!(normalize_str("hello \"world\""), Cow::<[u8]>::Owned(b"hello world".to_vec()));/// ```////// Escaped quotes are unescaped.////// ```/// # use std::borrow::Cow;/// # use git_config::values::normalize_str;/// assert_eq!(normalize_str(r#"hello "world\"""#), Cow::<[u8]>::Owned(br#"hello world""#.to_vec()));/// ```////// [`parser`]: crate::parser::Parser#[must_use]pub fn normalize_cow(input: Cow<'_, [u8]>) -> Cow<'_, [u8]> { let size = input.len(); if &*input == b"\"\"" { return Cow::Borrowed(&[]); } if size >= 3 && input[0] == b'=' && input[size - 1] == b'=' && input[size - 2] != b'\\' { match input { Cow::Borrowed(input) => return normalize_bytes(&input[1..size]), Cow::Owned(mut input) => { input.pop(); input.remove(0); return normalize_vec(input); } } } let mut owned = vec![]; let mut first_index = 0; let mut last_index = 0; let mut was_escaped = false; for (i, c) in input.iter().enumerate() { if was_escaped { was_escaped = false; if *c == b'"' { if first_index == 0 { owned.extend(&input[last_index..i - 1]); last_index = i; } else { owned.extend(&input[first_index..i - 1]); first_index = i; } } continue; } if *c == b'\\' { was_escaped = true; } else if *c == b'"' { if first_index == 0 { owned.extend(&input[last_index..i]); first_index = i + 1; } else { owned.extend(&input[first_index..i]); first_index = 0; last_index = i + 1; } } } if last_index == 0 { input } else { owned.extend(&input[last_index..]); Cow::Owned(owned) }} /// `&[u8]` variant of [`normalize_cow`].#[inline]#[must_use]pub fn normalize_bytes(input: &[u8]) -> Cow<'_, [u8]> { normalize_cow(Cow::Borrowed(input))} /// `Vec[u8]` variant of [`normalize_cow`].#[inline]#[must_use]pub fn normalize_vec(input: Vec<u8>) -> Cow<'static, [u8]> { normalize_cow(Cow::Owned(input))} /// [`str`] variant of [`normalize_cow`].#[inline]#[must_use]pub fn normalize_str(input: &str) -> Cow<'_, [u8]> { normalize_bytes(input.as_bytes())} /// Fully enumerated valid types that a `git-config` value can be.#[allow(missing_docs)]#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]pub enum Value<'a> { Boolean(Boolean<'a>), Integer(Integer), Color(Color), /// If a value does not match from any of the other variants, then this /// variant will be matched. As a result, conversion from a `str`-like item /// will never fail. Other(Cow<'a, [u8]>),} impl Value<'_> { /// Generates a byte representation of the value. This should be used when /// non-UTF-8 sequences are present or a UTF-8 representation can't be /// guaranteed. #[inline] #[must_use] pub fn to_vec(&self) -> Vec<u8> { self.into() }} impl<'a> From<&'a str> for Value<'a> { fn from(s: &'a str) -> Self { if let Ok(bool) = Boolean::try_from(s) { return Self::Boolean(bool); } if let Ok(int) = Integer::from_str(s) { return Self::Integer(int); } if let Ok(color) = Color::from_str(s) { return Self::Color(color); } Self::Other(Cow::Borrowed(s.as_bytes())) }} impl<'a> From<&'a [u8]> for Value<'a> { #[inline] fn from(s: &'a [u8]) -> Self { // All parsable values must be utf-8 valid if let Ok(s) = std::str::from_utf8(s) { Self::from(s) } else { Self::Other(Cow::Borrowed(s)) } }} impl From<String> for Value<'_> { #[inline] fn from(s: String) -> Self { Self::from(s.into_bytes()) }} impl From<Vec<u8>> for Value<'_> { fn from(s: Vec<u8>) -> Self { if let Ok(int) = Integer::try_from(s.as_ref()) { return Self::Integer(int); } if let Ok(color) = Color::try_from(s.as_ref()) { return Self::Color(color); } Boolean::try_from(s).map_or_else(|v| Self::Other(Cow::Owned(v)), Self::Boolean) }} impl<'a> From<Cow<'a, [u8]>> for Value<'a> { #[inline] fn from(c: Cow<'a, [u8]>) -> Self { match c { Cow::Borrowed(c) => Self::from(c), Cow::Owned(c) => Self::from(c), } }} impl From<Value<'_>> for Vec<u8> { #[inline] fn from(v: Value) -> Self { v.into() }} impl From<&Value<'_>> for Vec<u8> { #[inline] fn from(v: &Value) -> Self { match v { Value::Boolean(b) => b.into(), Value::Integer(i) => i.into(), Value::Color(c) => c.into(), Value::Other(o) => o.to_vec(), } }} impl Display for Value<'_> { /// Note that this is a best-effort attempt at printing a `Value`. If there /// are non UTF-8 values in your config, this will _NOT_ render as read. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Value::Boolean(b) => b.fmt(f), Value::Integer(i) => i.fmt(f), Value::Color(c) => c.fmt(f), Value::Other(o) => match std::str::from_utf8(o) { Ok(v) => v.fmt(f), Err(_) => write!(f, "{:?}", o), }, } }} #[cfg(feature = "serde")]impl Serialize for Value<'_> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { match self { Value::Boolean(b) => b.serialize(serializer), Value::Integer(i) => i.serialize(serializer), Value::Color(c) => c.serialize(serializer), Value::Other(i) => i.serialize(serializer), } }} /// Any value that can be interpreted as a boolean.////// Note that while values can effectively be any byte string, the `git-config`/// documentation has a strict subset of values that may be interpreted as a/// boolean value, all of which are ASCII and thus UTF-8 representable./// Consequently, variants hold [`str`]s rather than [`[u8]`]s.#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]#[allow(missing_docs)]pub enum Boolean<'a> { True(TrueVariant<'a>), False(Cow<'a, str>),} impl Boolean<'_> { /// Generates a byte representation of the value. This should be used when /// non-UTF-8 sequences are present or a UTF-8 representation can't be /// guaranteed. #[inline] #[must_use] pub fn to_vec(&self) -> Vec<u8> { self.into() } /// Generates a byte representation of the value. This should be used when /// non-UTF-8 sequences are present or a UTF-8 representation can't be /// guaranteed. #[inline] #[must_use] pub fn as_bytes(&self) -> &[u8] { self.into() }} impl<'a> TryFrom<&'a str> for Boolean<'a> { type Error = (); #[inline] fn try_from(value: &'a str) -> Result<Self, Self::Error> { Self::try_from(value.as_bytes()) }} impl<'a> TryFrom<&'a [u8]> for Boolean<'a> { type Error = (); fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { if let Ok(v) = TrueVariant::try_from(value) { return Ok(Self::True(v)); } if value.eq_ignore_ascii_case(b"no") || value.eq_ignore_ascii_case(b"off") || value.eq_ignore_ascii_case(b"false") || value.eq_ignore_ascii_case(b"zero") || value == b"\"\"" { return Ok(Self::False(std::str::from_utf8(value).unwrap().into())); } Err(()) }} impl TryFrom<String> for Boolean<'_> { type Error = String; fn try_from(value: String) -> Result<Self, Self::Error> { Self::try_from(value.into_bytes()).map_err(|v| String::from_utf8(v).unwrap()) }} impl TryFrom<Vec<u8>> for Boolean<'_> { type Error = Vec<u8>; fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { if value.eq_ignore_ascii_case(b"no") || value.eq_ignore_ascii_case(b"off") || value.eq_ignore_ascii_case(b"false") || value.eq_ignore_ascii_case(b"zero") || value == b"\"\"" { return Ok(Self::False(Cow::Owned(String::from_utf8(value).unwrap()))); } TrueVariant::try_from(value).map(Self::True) }} impl<'a> TryFrom<Cow<'a, [u8]>> for Boolean<'a> { type Error = (); fn try_from(c: Cow<'a, [u8]>) -> Result<Self, Self::Error> { match c { Cow::Borrowed(c) => Self::try_from(c), Cow::Owned(c) => Self::try_from(c).map_err(|_| ()), } }} impl Display for Boolean<'_> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Boolean::True(v) => v.fmt(f), Boolean::False(v) => write!(f, "{}", v), } }} impl From<Boolean<'_>> for bool { #[inline] fn from(b: Boolean) -> Self { match b { Boolean::True(_) => true, Boolean::False(_) => false, } }} impl<'a, 'b: 'a> From<&'b Boolean<'a>> for &'a [u8] { #[inline] fn from(b: &'b Boolean) -> Self { match b { Boolean::True(t) => t.into(), Boolean::False(f) => f.as_bytes(), } }} impl From<Boolean<'_>> for Vec<u8> { #[inline] fn from(b: Boolean) -> Self { b.into() }} impl From<&Boolean<'_>> for Vec<u8> { #[inline] fn from(b: &Boolean) -> Self { b.to_string().into_bytes() }} #[cfg(feature = "serde")]impl Serialize for Boolean<'_> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { match self { Boolean::True(_) => serializer.serialize_bool(true), Boolean::False(_) => serializer.serialize_bool(false), } }} /// Discriminating enum between implicit and explicit truthy values.////// This enum is part of the [`Boolean`] struct.#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]#[allow(missing_docs)]pub enum TrueVariant<'a> { Explicit(Cow<'a, str>), /// For values defined without a `= <value>`. Implicit,} impl<'a> TryFrom<&'a str> for TrueVariant<'a> { type Error = (); #[inline] fn try_from(value: &'a str) -> Result<Self, Self::Error> { Self::try_from(value.as_bytes()) }} impl<'a> TryFrom<&'a [u8]> for TrueVariant<'a> { type Error = (); fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { if value.eq_ignore_ascii_case(b"yes") || value.eq_ignore_ascii_case(b"on") || value.eq_ignore_ascii_case(b"true") || value.eq_ignore_ascii_case(b"one") { Ok(Self::Explicit(std::str::from_utf8(value).unwrap().into())) } else if value.is_empty() { Ok(Self::Implicit) } else { Err(()) } }} impl TryFrom<String> for TrueVariant<'_> { type Error = String; #[inline] fn try_from(value: String) -> Result<Self, Self::Error> { Self::try_from(value.into_bytes()).map_err(|v| String::from_utf8(v).unwrap()) }} impl TryFrom<Vec<u8>> for TrueVariant<'_> { type Error = Vec<u8>; fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { if value.eq_ignore_ascii_case(b"yes") || value.eq_ignore_ascii_case(b"on") || value.eq_ignore_ascii_case(b"true") || value.eq_ignore_ascii_case(b"one") { Ok(Self::Explicit(Cow::Owned(String::from_utf8(value).unwrap()))) } else if value.is_empty() { Ok(Self::Implicit) } else { Err(value) } }} impl Display for TrueVariant<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Self::Explicit(v) = self {Lines 1–500 of 1391. Load more
//! Rust containers for valid `git-config` types. #[cfg(feature = "serde")] use serde::{Serialize, Serializer}; use std::borrow::Cow; use std::convert::TryFrom; use std::fmt::Display; use std::str::FromStr; /// Removes quotes, if any, from the provided inputs. This assumes the input /// contains a even number of unescaped quotes, and will unescape escaped /// quotes. The return values should be safe for value interpretation. /// /// This has optimizations for fully-quoted values, where the returned value /// will be a borrowed reference if the only mutation necessary is to unquote /// the value. /// /// This is the function used to normalize raw values from higher level /// abstractions over the [`parser`] implementation. Generally speaking these /// high level abstractions will handle normalization for you, and you do not /// need to call this yourself. However, if you're directly handling events /// from the parser, you may want to use this to help with value interpretation. /// /// Generally speaking, you'll want to use one of the variants of this function, /// such as [`normalize_str`] or [`normalize_vec`]. /// /// # Examples /// /// Values don't need modification are returned borrowed, without allocation. /// /// ``` /// # use std::borrow::Cow; /// # use git_config::values::normalize_str; /// assert_eq!(normalize_str("hello world"), Cow::Borrowed(b"hello world".into())); /// ``` /// /// Fully quoted values are optimized to not need allocations. /// /// ``` /// # use std::borrow::Cow; /// # use git_config::values::normalize_str; /// assert_eq!(normalize_str("\"hello world\""), Cow::Borrowed(b"hello world".into())); /// ``` /// /// Quoted values are unwrapped as an owned variant. /// /// ``` /// # use std::borrow::Cow; /// # use git_config::values::normalize_str; /// assert_eq!(normalize_str("hello \"world\""), Cow::<[u8]>::Owned(b"hello world".to_vec())); /// ``` /// /// Escaped quotes are unescaped. /// /// ``` /// # use std::borrow::Cow; /// # use git_config::values::normalize_str; /// assert_eq!(normalize_str(r#"hello "world\"""#), Cow::<[u8]>::Owned(br#"hello world""#.to_vec())); /// ``` /// /// [`parser`]: crate::parser::Parser #[must_use] pub fn normalize_cow(input: Cow<'_, [u8]>) -> Cow<'_, [u8]> { let size = input.len(); if &*input == b"\"\"" { return Cow::Borrowed(&[]); } if size >= 3 && input[0] == b'=' && input[size - 1] == b'=' && input[size - 2] != b'\\' { match input { Cow::Borrowed(input) => return normalize_bytes(&input[1..size]), Cow::Owned(mut input) => { input.pop(); input.remove(0); return normalize_vec(input); } } } let mut owned = vec![]; let mut first_index = 0; let mut last_index = 0; let mut was_escaped = false; for (i, c) in input.iter().enumerate() { if was_escaped { was_escaped = false; if *c == b'"' { if first_index == 0 { owned.extend(&input[last_index..i - 1]); last_index = i; } else { owned.extend(&input[first_index..i - 1]); first_index = i; } } continue; } if *c == b'\\' { was_escaped = true; } else if *c == b'"' { if first_index == 0 { owned.extend(&input[last_index..i]); first_index = i + 1; } else { owned.extend(&input[first_index..i]); first_index = 0; last_index = i + 1; } } } if last_index == 0 { input } else { owned.extend(&input[last_index..]); Cow::Owned(owned) } } /// `&[u8]` variant of [`normalize_cow`]. #[inline] #[must_use] pub fn normalize_bytes(input: &[u8]) -> Cow<'_, [u8]> { normalize_cow(Cow::Borrowed(input)) } /// `Vec[u8]` variant of [`normalize_cow`]. #[inline] #[must_use] pub fn normalize_vec(input: Vec<u8>) -> Cow<'static, [u8]> { normalize_cow(Cow::Owned(input)) } /// [`str`] variant of [`normalize_cow`]. #[inline] #[must_use] pub fn normalize_str(input: &str) -> Cow<'_, [u8]> { normalize_bytes(input.as_bytes()) } /// Fully enumerated valid types that a `git-config` value can be. #[allow(missing_docs)] #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] pub enum Value<'a> { Boolean(Boolean<'a>), Integer(Integer), Color(Color), /// If a value does not match from any of the other variants, then this /// variant will be matched. As a result, conversion from a `str`-like item /// will never fail. Other(Cow<'a, [u8]>), } impl Value<'_> { /// Generates a byte representation of the value. This should be used when /// non-UTF-8 sequences are present or a UTF-8 representation can't be /// guaranteed. #[inline] #[must_use] pub fn to_vec(&self) -> Vec<u8> { self.into() } } impl<'a> From<&'a str> for Value<'a> { fn from(s: &'a str) -> Self { if let Ok(bool) = Boolean::try_from(s) { return Self::Boolean(bool); } if let Ok(int) = Integer::from_str(s) { return Self::Integer(int); } if let Ok(color) = Color::from_str(s) { return Self::Color(color); } Self::Other(Cow::Borrowed(s.as_bytes())) } } impl<'a> From<&'a [u8]> for Value<'a> { #[inline] fn from(s: &'a [u8]) -> Self { // All parsable values must be utf-8 valid if let Ok(s) = std::str::from_utf8(s) { Self::from(s) } else { Self::Other(Cow::Borrowed(s)) } } } impl From<String> for Value<'_> { #[inline] fn from(s: String) -> Self { Self::from(s.into_bytes()) } } impl From<Vec<u8>> for Value<'_> { fn from(s: Vec<u8>) -> Self { if let Ok(int) = Integer::try_from(s.as_ref()) { return Self::Integer(int); } if let Ok(color) = Color::try_from(s.as_ref()) { return Self::Color(color); } Boolean::try_from(s).map_or_else(|v| Self::Other(Cow::Owned(v)), Self::Boolean) } } impl<'a> From<Cow<'a, [u8]>> for Value<'a> { #[inline] fn from(c: Cow<'a, [u8]>) -> Self { match c { Cow::Borrowed(c) => Self::from(c), Cow::Owned(c) => Self::from(c), } } } impl From<Value<'_>> for Vec<u8> { #[inline] fn from(v: Value) -> Self { v.into() } } impl From<&Value<'_>> for Vec<u8> { #[inline] fn from(v: &Value) -> Self { match v { Value::Boolean(b) => b.into(), Value::Integer(i) => i.into(), Value::Color(c) => c.into(), Value::Other(o) => o.to_vec(), } } } impl Display for Value<'_> { /// Note that this is a best-effort attempt at printing a `Value`. If there /// are non UTF-8 values in your config, this will _NOT_ render as read. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Value::Boolean(b) => b.fmt(f), Value::Integer(i) => i.fmt(f), Value::Color(c) => c.fmt(f), Value::Other(o) => match std::str::from_utf8(o) { Ok(v) => v.fmt(f), Err(_) => write!(f, "{:?}", o), }, } } } #[cfg(feature = "serde")] impl Serialize for Value<'_> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { match self { Value::Boolean(b) => b.serialize(serializer), Value::Integer(i) => i.serialize(serializer), Value::Color(c) => c.serialize(serializer), Value::Other(i) => i.serialize(serializer), } } } /// Any value that can be interpreted as a boolean. /// /// Note that while values can effectively be any byte string, the `git-config` /// documentation has a strict subset of values that may be interpreted as a /// boolean value, all of which are ASCII and thus UTF-8 representable. /// Consequently, variants hold [`str`]s rather than [`[u8]`]s. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[allow(missing_docs)] pub enum Boolean<'a> { True(TrueVariant<'a>), False(Cow<'a, str>), } impl Boolean<'_> { /// Generates a byte representation of the value. This should be used when /// non-UTF-8 sequences are present or a UTF-8 representation can't be /// guaranteed. #[inline] #[must_use] pub fn to_vec(&self) -> Vec<u8> { self.into() } /// Generates a byte representation of the value. This should be used when /// non-UTF-8 sequences are present or a UTF-8 representation can't be /// guaranteed. #[inline] #[must_use] pub fn as_bytes(&self) -> &[u8] { self.into() } } impl<'a> TryFrom<&'a str> for Boolean<'a> { type Error = (); #[inline] fn try_from(value: &'a str) -> Result<Self, Self::Error> { Self::try_from(value.as_bytes()) } } impl<'a> TryFrom<&'a [u8]> for Boolean<'a> { type Error = (); fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { if let Ok(v) = TrueVariant::try_from(value) { return Ok(Self::True(v)); } if value.eq_ignore_ascii_case(b"no") || value.eq_ignore_ascii_case(b"off") || value.eq_ignore_ascii_case(b"false") || value.eq_ignore_ascii_case(b"zero") || value == b"\"\"" { return Ok(Self::False(std::str::from_utf8(value).unwrap().into())); } Err(()) } } impl TryFrom<String> for Boolean<'_> { type Error = String; fn try_from(value: String) -> Result<Self, Self::Error> { Self::try_from(value.into_bytes()).map_err(|v| String::from_utf8(v).unwrap()) } } impl TryFrom<Vec<u8>> for Boolean<'_> { type Error = Vec<u8>; fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { if value.eq_ignore_ascii_case(b"no") || value.eq_ignore_ascii_case(b"off") || value.eq_ignore_ascii_case(b"false") || value.eq_ignore_ascii_case(b"zero") || value == b"\"\"" { return Ok(Self::False(Cow::Owned(String::from_utf8(value).unwrap()))); } TrueVariant::try_from(value).map(Self::True) } } impl<'a> TryFrom<Cow<'a, [u8]>> for Boolean<'a> { type Error = (); fn try_from(c: Cow<'a, [u8]>) -> Result<Self, Self::Error> { match c { Cow::Borrowed(c) => Self::try_from(c), Cow::Owned(c) => Self::try_from(c).map_err(|_| ()), } } } impl Display for Boolean<'_> { #[inline] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Boolean::True(v) => v.fmt(f), Boolean::False(v) => write!(f, "{}", v), } } } impl From<Boolean<'_>> for bool { #[inline] fn from(b: Boolean) -> Self { match b { Boolean::True(_) => true, Boolean::False(_) => false, } } } impl<'a, 'b: 'a> From<&'b Boolean<'a>> for &'a [u8] { #[inline] fn from(b: &'b Boolean) -> Self { match b { Boolean::True(t) => t.into(), Boolean::False(f) => f.as_bytes(), } } } impl From<Boolean<'_>> for Vec<u8> { #[inline] fn from(b: Boolean) -> Self { b.into() } } impl From<&Boolean<'_>> for Vec<u8> { #[inline] fn from(b: &Boolean) -> Self { b.to_string().into_bytes() } } #[cfg(feature = "serde")] impl Serialize for Boolean<'_> { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, { match self { Boolean::True(_) => serializer.serialize_bool(true), Boolean::False(_) => serializer.serialize_bool(false), } } } /// Discriminating enum between implicit and explicit truthy values. /// /// This enum is part of the [`Boolean`] struct. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[allow(missing_docs)] pub enum TrueVariant<'a> { Explicit(Cow<'a, str>), /// For values defined without a `= <value>`. Implicit, } impl<'a> TryFrom<&'a str> for TrueVariant<'a> { type Error = (); #[inline] fn try_from(value: &'a str) -> Result<Self, Self::Error> { Self::try_from(value.as_bytes()) } } impl<'a> TryFrom<&'a [u8]> for TrueVariant<'a> { type Error = (); fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> { if value.eq_ignore_ascii_case(b"yes") || value.eq_ignore_ascii_case(b"on") || value.eq_ignore_ascii_case(b"true") || value.eq_ignore_ascii_case(b"one") { Ok(Self::Explicit(std::str::from_utf8(value).unwrap().into())) } else if value.is_empty() { Ok(Self::Implicit) } else { Err(()) } } } impl TryFrom<String> for TrueVariant<'_> { type Error = String; #[inline] fn try_from(value: String) -> Result<Self, Self::Error> { Self::try_from(value.into_bytes()).map_err(|v| String::from_utf8(v).unwrap()) } } impl TryFrom<Vec<u8>> for TrueVariant<'_> { type Error = Vec<u8>; fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> { if value.eq_ignore_ascii_case(b"yes") || value.eq_ignore_ascii_case(b"on") || value.eq_ignore_ascii_case(b"true") || value.eq_ignore_ascii_case(b"one") { Ok(Self::Explicit(Cow::Owned(String::from_utf8(value).unwrap()))) } else if value.is_empty() { Ok(Self::Implicit) } else { Err(value) } } } impl Display for TrueVariant<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Self::Explicit(v) = self {