Compare commits

..

3 commits

Author SHA1 Message Date
65744b0e13
fully comment values 2021-02-27 19:55:19 -05:00
37cead20f3
more normalize docs 2021-02-27 19:14:49 -05:00
267c53f15d
collaspe if block 2021-02-27 19:07:50 -05:00
2 changed files with 103 additions and 52 deletions

View file

@ -16,7 +16,7 @@ serde = ["serde_crate"]
[dependencies] [dependencies]
bstr = "0.2.15" bstr = "0.2.15"
nom = { version = "6", default_features = false, features = ["std"] } nom = { version = "6", default_features = false, features = ["std"] }
serde_crate = { version = "1", package = "serde", optional = true} serde_crate = { version = "1", package = "serde", optional = true }
[dev-dependencies] [dev-dependencies]
serde_derive = "1.0" serde_derive = "1.0"

View file

@ -9,13 +9,19 @@ use std::fmt::Display;
use std::str::FromStr; use std::str::FromStr;
/// Removes quotes, if any, from the provided inputs. This assumes the input /// Removes quotes, if any, from the provided inputs. This assumes the input
/// contains a even number of unescaped quotes, and will unescape escaped quotes. /// contains a even number of unescaped quotes, and will unescape escaped
/// The return values should be safe for value interpretation. /// quotes. The return values should be safe for value interpretation.
/// ///
/// This has optimizations for fully-quoted values, where the returned value /// This has optimizations for fully-quoted values, where the returned value
/// will be a borrowed reference if the only mutation necessary is to unquote /// will be a borrowed reference if the only mutation necessary is to unquote
/// the value. /// 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.
///
/// # Examples /// # Examples
/// ///
/// Values don't need modification are returned borrowed, without allocation. /// Values don't need modification are returned borrowed, without allocation.
@ -49,6 +55,8 @@ use std::str::FromStr;
/// # use git_config::values::normalize; /// # use git_config::values::normalize;
/// assert_eq!(normalize(br#"hello "world\"""#), Cow::<[u8]>::Owned(br#"hello world""#.to_vec())); /// assert_eq!(normalize(br#"hello "world\"""#), Cow::<[u8]>::Owned(br#"hello world""#.to_vec()));
/// ``` /// ```
///
/// [`parser`]: crate::parser::Parser
pub fn normalize(input: &[u8]) -> Cow<'_, [u8]> { pub fn normalize(input: &[u8]) -> Cow<'_, [u8]> {
let mut first_index = 0; let mut first_index = 0;
let mut last_index = 0; let mut last_index = 0;
@ -59,11 +67,9 @@ pub fn normalize(input: &[u8]) -> Cow<'_, [u8]> {
return Cow::Borrowed(&[]); return Cow::Borrowed(&[]);
} }
if size >= 3 { if size >= 3 && input[0] == b'=' && input[size - 1] == b'=' && input[size - 2] != b'\\' {
if input[0] == b'=' && input[size - 1] == b'=' && input[size - 2] != b'\\' {
return normalize(&input[1..size]); return normalize(&input[1..size]);
} }
}
let mut owned = vec![]; let mut owned = vec![];
@ -118,18 +124,6 @@ pub enum Value<'a> {
Other(Cow<'a, BStr>), Other(Cow<'a, BStr>),
} }
impl<'a> Value<'a> {
pub fn from_string(s: String) -> Self {
Self::Other(Cow::Owned(s.into()))
}
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum ValueEventConversionError {
ValueNotDone,
NoValue,
}
impl<'a> From<&'a str> for Value<'a> { impl<'a> From<&'a str> for Value<'a> {
fn from(s: &'a str) -> Self { fn from(s: &'a str) -> Self {
if let Ok(bool) = Boolean::try_from(s) { if let Ok(bool) = Boolean::try_from(s) {
@ -159,12 +153,6 @@ impl<'a> From<&'a [u8]> for Value<'a> {
} }
} }
// impl From<Vec<u8>> for Value<'_> {
// fn from(_: Vec<u8>) -> Self {
// todo!()
// }
// }
// todo display for value // todo display for value
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
@ -182,7 +170,16 @@ impl Serialize for Value<'_> {
} }
} }
/// 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 [`BStr`]s.
///
/// [`BStr`]: bstr::BStr
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[allow(missing_docs)]
pub enum Boolean<'a> { pub enum Boolean<'a> {
True(TrueVariant<'a>), True(TrueVariant<'a>),
False(&'a str), False(&'a str),
@ -248,13 +245,14 @@ impl Serialize for Boolean<'_> {
} }
} }
/// Discriminating enum between implicit and explicit truthy values.
///
/// This enum is part of the [`Boolean`] struct.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[allow(missing_docs)]
pub enum TrueVariant<'a> { pub enum TrueVariant<'a> {
Explicit(&'a str), Explicit(&'a str),
/// For variables defined without a `= <value>`. This can never be created /// For values defined without a `= <value>`.
/// from the [`FromStr`] trait, as an empty string is false without context.
/// If directly serializing this struct (instead of using a higher level
/// wrapper), then this variant is serialized as if it was `true`.
Implicit, Implicit,
} }
@ -304,6 +302,17 @@ impl Serialize for TrueVariant<'_> {
} }
} }
/// Any value that can be interpreted as an integer.
///
/// This supports any numeric value that can fit in a [`i64`], excluding the
/// suffix. The suffix is parsed separately from the value itself, so if you
/// wish to obtain the true value of the integer, you must account for the
/// suffix after fetching the value. [`IntegerSuffix`] provides
/// [`bitwise_offset`] to help with the math, but do be warned that if the value
/// is very large, you may run into overflows.
///
/// [`BStr`]: bstr::BStr
/// [`bitwise_offset`]: IntegerSuffix::bitwise_offset
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct Integer { pub struct Integer {
value: i64, value: i64,
@ -372,20 +381,24 @@ impl TryFrom<&[u8]> for Integer {
} }
} }
/// Integer prefixes that are supported by `git-config`.
///
/// These values are base-2 unit of measurements, not the base-10 variants.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[allow(missing_docs)]
pub enum IntegerSuffix { pub enum IntegerSuffix {
Kilo, Kibi,
Mega, Mebi,
Giga, Gibi,
} }
impl IntegerSuffix { impl IntegerSuffix {
/// Returns the number of bits that the suffix shifts left by. /// Returns the number of bits that the suffix shifts left by.
pub fn bitwise_offset(&self) -> usize { pub fn bitwise_offset(&self) -> usize {
match self { match self {
Self::Kilo => 10, Self::Kibi => 10,
Self::Mega => 20, Self::Mebi => 20,
Self::Giga => 30, Self::Gibi => 30,
} }
} }
} }
@ -393,9 +406,9 @@ impl IntegerSuffix {
impl Display for IntegerSuffix { impl Display for IntegerSuffix {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
Self::Kilo => write!(f, "k"), Self::Kibi => write!(f, "k"),
Self::Mega => write!(f, "m"), Self::Mebi => write!(f, "m"),
Self::Giga => write!(f, "g"), Self::Gibi => write!(f, "g"),
} }
} }
} }
@ -407,9 +420,9 @@ impl Serialize for IntegerSuffix {
S: Serializer, S: Serializer,
{ {
serializer.serialize_str(match self { serializer.serialize_str(match self {
Self::Kilo => "k", Self::Kibi => "k",
Self::Mega => "m", Self::Mebi => "m",
Self::Giga => "g", Self::Gibi => "g",
}) })
} }
} }
@ -419,9 +432,9 @@ impl FromStr for IntegerSuffix {
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
match s { match s {
"k" => Ok(Self::Kilo), "k" => Ok(Self::Kibi),
"m" => Ok(Self::Mega), "m" => Ok(Self::Mebi),
"g" => Ok(Self::Giga), "g" => Ok(Self::Gibi),
_ => Err(()), _ => Err(()),
} }
} }
@ -435,6 +448,13 @@ impl TryFrom<&[u8]> for IntegerSuffix {
} }
} }
/// Any value that may contain a foreground color, background color, a
/// collection of color (text) modifiers, or a combination of any of the
/// aforementioned values.
///
/// Note that `git-config` allows color values to simply be a collection of
/// [`ColorAttribute`]s, and does not require a [`ColorValue`] for either the
/// foreground or background color.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)] #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Color { pub struct Color {
foreground: Option<ColorValue>, foreground: Option<ColorValue>,
@ -442,6 +462,23 @@ pub struct Color {
attributes: Vec<ColorAttribute>, attributes: Vec<ColorAttribute>,
} }
impl Color {
/// Returns the foreground color, if any.
pub fn foreground(&self) -> Option<ColorValue> {
self.foreground
}
/// Returns the background color, if any.
pub fn background(&self) -> Option<ColorValue> {
self.background
}
/// Returns the list of text modifiers, if any.
pub fn attributes(&self) -> &[ColorAttribute] {
&self.attributes
}
}
impl Display for Color { impl Display for Color {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(fg) = self.foreground { if let Some(fg) = self.foreground {
@ -471,13 +508,16 @@ impl Serialize for Color {
} }
} }
pub enum FromColorErr { /// Discriminating enum for [`Color`] parsing.
pub enum ColorParseError {
/// Too many primary colors were provided.
TooManyColorValues, TooManyColorValues,
/// An invalid color value or attribute was provided.
InvalidColorOption, InvalidColorOption,
} }
impl FromStr for Color { impl FromStr for Color {
type Err = FromColorErr; type Err = ColorParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
enum ColorItem { enum ColorItem {
@ -507,12 +547,12 @@ impl FromStr for Color {
} else if new_self.background.is_none() { } else if new_self.background.is_none() {
new_self.background = Some(v); new_self.background = Some(v);
} else { } else {
return Err(FromColorErr::TooManyColorValues); return Err(ColorParseError::TooManyColorValues);
} }
} }
ColorItem::Attr(a) => new_self.attributes.push(a), ColorItem::Attr(a) => new_self.attributes.push(a),
}, },
Err(_) => return Err(FromColorErr::InvalidColorOption), Err(_) => return Err(ColorParseError::InvalidColorOption),
} }
} }
@ -528,8 +568,13 @@ impl TryFrom<&[u8]> for Color {
} }
} }
/// Discriminating enum for [`Color`] values.
///
/// `git-config` supports the eight standard colors, their bright variants, an
/// ANSI color code, or a 24-bit hex value prefixed with an octothorpe.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
enum ColorValue { #[allow(missing_docs)]
pub enum ColorValue {
Normal, Normal,
Black, Black,
BrightBlack, BrightBlack,
@ -651,7 +696,13 @@ impl TryFrom<&[u8]> for ColorValue {
} }
} }
/// Discriminating enum for [`Color`] attributes.
///
/// `git-config` supports modifiers and their negators. The negating color
/// attributes are equivalent to having a `no` or `no-` prefix to the normal
/// variant.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[allow(missing_docs)]
pub enum ColorAttribute { pub enum ColorAttribute {
Bold, Bold,
NoBold, NoBold,
@ -895,7 +946,7 @@ mod integer {
Integer::from_str("1k").unwrap(), Integer::from_str("1k").unwrap(),
Integer { Integer {
value: 1, value: 1,
suffix: Some(IntegerSuffix::Kilo), suffix: Some(IntegerSuffix::Kibi),
} }
); );
@ -903,7 +954,7 @@ mod integer {
Integer::from_str("1m").unwrap(), Integer::from_str("1m").unwrap(),
Integer { Integer {
value: 1, value: 1,
suffix: Some(IntegerSuffix::Mega), suffix: Some(IntegerSuffix::Mebi),
} }
); );
@ -911,7 +962,7 @@ mod integer {
Integer::from_str("1g").unwrap(), Integer::from_str("1g").unwrap(),
Integer { Integer {
value: 1, value: 1,
suffix: Some(IntegerSuffix::Giga), suffix: Some(IntegerSuffix::Gibi),
} }
); );
} }