Compare commits
3 commits
60f95a0358
...
65744b0e13
Author | SHA1 | Date | |
---|---|---|---|
65744b0e13 | |||
37cead20f3 | |||
267c53f15d |
2 changed files with 103 additions and 52 deletions
|
@ -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"
|
||||||
|
|
153
src/values.rs
153
src/values.rs
|
@ -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,10 +67,8 @@ 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),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue