1383 lines · 39.6 KB · Rust
RawDownload
//! 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.    #[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> {    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 = ();     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;     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 {            write!(f, "{}", v)        } else {            Ok(())        }

Lines 1–500 of 1383.