Compare commits
No commits in common. "1cde32efd194aaba6ae6a2263c61c6f56c02b380" and "ca55bff79de3bf6e2aa86745b7a90a8f72b8fddf" have entirely different histories.
1cde32efd1
...
ca55bff79d
2 changed files with 166 additions and 378 deletions
512
src/file.rs
512
src/file.rs
|
@ -4,7 +4,8 @@ use crate::parser::{
|
||||||
parse_from_bytes, Error, Event, Key, ParsedSectionHeader, Parser, SectionHeaderName,
|
parse_from_bytes, Error, Event, Key, ParsedSectionHeader, Parser, SectionHeaderName,
|
||||||
};
|
};
|
||||||
use crate::values::{normalize_bytes, normalize_cow, normalize_vec};
|
use crate::values::{normalize_bytes, normalize_cow, normalize_vec};
|
||||||
use std::borrow::{Borrow, Cow};
|
use std::borrow::Borrow;
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::{HashMap, VecDeque};
|
use std::collections::{HashMap, VecDeque};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -18,9 +19,9 @@ pub enum GitConfigError<'a> {
|
||||||
/// The requested subsection does not exist.
|
/// The requested subsection does not exist.
|
||||||
SubSectionDoesNotExist(Option<&'a str>),
|
SubSectionDoesNotExist(Option<&'a str>),
|
||||||
/// The key does not exist in the requested section.
|
/// The key does not exist in the requested section.
|
||||||
KeyDoesNotExist,
|
KeyDoesNotExist(Key<'a>),
|
||||||
/// The conversion into the provided type for methods such as
|
/// The conversion into the provided type for methods such as
|
||||||
/// [`GitConfig::value`] failed.
|
/// [`GitConfig::get_value`] failed.
|
||||||
FailedConversion,
|
FailedConversion,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +33,7 @@ impl Display for GitConfigError<'_> {
|
||||||
Some(s) => write!(f, "Subsection '{}' does not exist.", s),
|
Some(s) => write!(f, "Subsection '{}' does not exist.", s),
|
||||||
None => write!(f, "Top level section does not exist."),
|
None => write!(f, "Top level section does not exist."),
|
||||||
},
|
},
|
||||||
Self::KeyDoesNotExist => write!(f, "The name for a value provided does not exist."),
|
Self::KeyDoesNotExist(k) => write!(f, "Name '{}' does not exist.", k),
|
||||||
Self::FailedConversion => write!(f, "Failed to convert to specified type."),
|
Self::FailedConversion => write!(f, "Failed to convert to specified type."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,122 +55,46 @@ impl std::error::Error for GitConfigError<'_> {}
|
||||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord, Debug)]
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, PartialOrd, Ord, Debug)]
|
||||||
struct SectionId(usize);
|
struct SectionId(usize);
|
||||||
|
|
||||||
/// A opaque type that represents a mutable reference to a section.
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
enum LookupTreeNode<'a> {
|
||||||
pub struct MutableSection<'borrow, 'event>(&'borrow mut Vec<Event<'event>>);
|
Terminal(Vec<SectionId>),
|
||||||
|
NonTerminal(HashMap<Cow<'a, str>, Vec<SectionId>>),
|
||||||
|
}
|
||||||
|
|
||||||
// Immutable methods, effectively a deref into Section
|
/// An intermediate representation of a mutable value obtained from
|
||||||
impl<'borrow, 'event> MutableSection<'borrow, 'event> {
|
/// [`GitConfig`].
|
||||||
/// Retrieves the last matching value in a section with the given key.
|
///
|
||||||
/// Returns None if the key was not found.
|
/// This holds a mutable reference to the underlying data structure of
|
||||||
#[inline]
|
/// [`GitConfig`], and thus guarantees through Rust's borrower checker that
|
||||||
#[must_use]
|
/// multiple mutable references to [`GitConfig`] cannot be owned at the same
|
||||||
pub fn value(&self, key: &Key) -> Option<Cow<'event, [u8]>> {
|
/// time.
|
||||||
Section(self.0).value(key)
|
pub struct MutableValue<'borrow, 'lookup, 'event> {
|
||||||
}
|
section: &'borrow mut Vec<Event<'event>>,
|
||||||
|
key: Key<'lookup>,
|
||||||
|
index: usize,
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves the last matching value in a section with the given key, and
|
impl MutableValue<'_, '_, '_> {
|
||||||
/// attempts to convert the value into the provided type.
|
/// Returns the actual value. This is computed each time this is called, so
|
||||||
|
/// it's best to reuse this value or own it if an allocation is acceptable.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
///
|
///
|
||||||
/// Returns an error if the key was not found, or if the conversion failed.
|
/// Returns an error if the lookup failed.
|
||||||
#[inline]
|
pub fn get(&self) -> Result<Cow<'_, [u8]>, GitConfigError> {
|
||||||
pub fn value_as<T: TryFrom<Cow<'event, [u8]>>>(
|
|
||||||
&self,
|
|
||||||
key: &Key,
|
|
||||||
) -> Result<T, GitConfigError<'event>> {
|
|
||||||
Section(self.0).value_as(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all values that have the provided key name. This may return
|
|
||||||
/// an empty vec, which implies there was values with the provided key.
|
|
||||||
#[inline]
|
|
||||||
#[must_use]
|
|
||||||
pub fn values(&self, key: &Key) -> Vec<Cow<'event, [u8]>> {
|
|
||||||
Section(self.0).values(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all values that have the provided key name. This may return
|
|
||||||
/// an empty vec, which implies there was values with the provided key.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the conversion failed.
|
|
||||||
#[inline]
|
|
||||||
pub fn values_as<T: TryFrom<Cow<'event, [u8]>>>(
|
|
||||||
&self,
|
|
||||||
key: &Key,
|
|
||||||
) -> Result<Vec<T>, GitConfigError<'event>> {
|
|
||||||
Section(self.0).values_as(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutable methods on a mutable section
|
|
||||||
impl<'borrow, 'event> MutableSection<'borrow, 'event> {
|
|
||||||
/// Adds an entry to the end of this section
|
|
||||||
pub fn push(&mut self, key: Key<'event>, value: Cow<'event, [u8]>) {
|
|
||||||
self.0.push(Event::Key(key));
|
|
||||||
self.0.push(Event::KeyValueSeparator);
|
|
||||||
self.0.push(Event::Value(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes all events until a key value pair is removed. This will also
|
|
||||||
/// remove the whitespace preceding the key value pair, if any is found.
|
|
||||||
pub fn pop(&mut self) -> Option<(Key, Cow<'event, [u8]>)> {
|
|
||||||
let mut values = vec![];
|
|
||||||
// events are popped in reverse order
|
|
||||||
while let Some(e) = self.0.pop() {
|
|
||||||
match e {
|
|
||||||
Event::Key(k) => {
|
|
||||||
// pop leading whitespace
|
|
||||||
if let Some(Event::Whitespace(_)) = self.0.last() {
|
|
||||||
self.0.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
if values.len() == 1 {
|
|
||||||
return Some((k, normalize_cow(values.pop().unwrap())));
|
|
||||||
}
|
|
||||||
return Some((
|
|
||||||
k,
|
|
||||||
normalize_vec(
|
|
||||||
values
|
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
.flat_map(|v: Cow<[u8]>| v.to_vec())
|
|
||||||
.collect(),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) => values.push(v),
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal methods that require exact indices for faster operations.
|
|
||||||
impl<'borrow, 'event> MutableSection<'borrow, 'event> {
|
|
||||||
fn get<'key>(
|
|
||||||
&self,
|
|
||||||
key: &Key<'key>,
|
|
||||||
start: usize,
|
|
||||||
end: usize,
|
|
||||||
) -> Result<Cow<'_, [u8]>, GitConfigError<'key>> {
|
|
||||||
let mut found_key = false;
|
let mut found_key = false;
|
||||||
let mut latest_value = None;
|
let mut latest_value = None;
|
||||||
let mut partial_value = None;
|
let mut partial_value = None;
|
||||||
// section_id is guaranteed to exist in self.sections, else we have a
|
// section_id is guaranteed to exist in self.sections, else we have a
|
||||||
// violated invariant.
|
// violated invariant.
|
||||||
|
|
||||||
for event in &self.0[start..=end] {
|
for event in &self.section[self.index..=self.index + self.size] {
|
||||||
match event {
|
match event {
|
||||||
Event::Key(event_key) if event_key == key => found_key = true,
|
Event::Key(event_key) if *event_key == self.key => found_key = true,
|
||||||
Event::Value(v) if found_key => {
|
Event::Value(v) if found_key => {
|
||||||
found_key = false;
|
found_key = false;
|
||||||
// Clones the Cow, doesn't copy underlying value if borrowed
|
latest_value = Some(Cow::Borrowed(v.borrow()));
|
||||||
latest_value = Some(v.clone());
|
|
||||||
}
|
}
|
||||||
Event::ValueNotDone(v) if found_key => {
|
Event::ValueNotDone(v) if found_key => {
|
||||||
latest_value = None;
|
latest_value = None;
|
||||||
|
@ -186,155 +111,7 @@ impl<'borrow, 'event> MutableSection<'borrow, 'event> {
|
||||||
latest_value
|
latest_value
|
||||||
.map(normalize_cow)
|
.map(normalize_cow)
|
||||||
.or_else(|| partial_value.map(normalize_vec))
|
.or_else(|| partial_value.map(normalize_vec))
|
||||||
.ok_or(GitConfigError::KeyDoesNotExist)
|
.ok_or(GitConfigError::KeyDoesNotExist(self.key.to_owned()))
|
||||||
}
|
|
||||||
|
|
||||||
fn delete(&mut self, start: usize, end: usize) {
|
|
||||||
self.0.drain(start..=end);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_value(&mut self, index: usize, key: Key<'event>, value: Vec<u8>) {
|
|
||||||
self.0.insert(index, Event::Value(Cow::Owned(value)));
|
|
||||||
self.0.insert(index, Event::KeyValueSeparator);
|
|
||||||
self.0.insert(index, Event::Key(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A opaque type that represents a reference to a section.
|
|
||||||
#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
|
||||||
pub struct Section<'borrow, 'event>(&'borrow [Event<'event>]);
|
|
||||||
|
|
||||||
impl<'borrow, 'event> Section<'borrow, 'event> {
|
|
||||||
/// Retrieves the last matching value in a section with the given key.
|
|
||||||
/// Returns None if the key was not found.
|
|
||||||
#[must_use]
|
|
||||||
pub fn value(&self, key: &Key) -> Option<Cow<'event, [u8]>> {
|
|
||||||
let mut found_key = false;
|
|
||||||
let mut latest_value = None;
|
|
||||||
let mut partial_value = None;
|
|
||||||
|
|
||||||
// todo: iterate backwards instead
|
|
||||||
for event in self.0 {
|
|
||||||
match event {
|
|
||||||
Event::Key(event_key) if *event_key == *key => found_key = true,
|
|
||||||
Event::Value(v) if found_key => {
|
|
||||||
found_key = false;
|
|
||||||
// Clones the Cow, doesn't copy underlying value if borrowed
|
|
||||||
latest_value = Some(v.clone());
|
|
||||||
partial_value = None;
|
|
||||||
}
|
|
||||||
Event::ValueNotDone(v) if found_key => {
|
|
||||||
latest_value = None;
|
|
||||||
partial_value = Some((*v).to_vec());
|
|
||||||
}
|
|
||||||
Event::ValueDone(v) if found_key => {
|
|
||||||
found_key = false;
|
|
||||||
partial_value.as_mut().unwrap().extend(&**v);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
latest_value.or_else(|| partial_value.map(normalize_vec))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves the last matching value in a section with the given key, and
|
|
||||||
/// attempts to convert the value into the provided type.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the key was not found, or if the conversion failed.
|
|
||||||
pub fn value_as<T: TryFrom<Cow<'event, [u8]>>>(
|
|
||||||
&self,
|
|
||||||
key: &Key,
|
|
||||||
) -> Result<T, GitConfigError<'event>> {
|
|
||||||
T::try_from(self.value(key).ok_or(GitConfigError::KeyDoesNotExist)?)
|
|
||||||
.map_err(|_| GitConfigError::FailedConversion)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all values that have the provided key name. This may return
|
|
||||||
/// an empty vec, which implies there was values with the provided key.
|
|
||||||
#[must_use]
|
|
||||||
pub fn values(&self, key: &Key) -> Vec<Cow<'event, [u8]>> {
|
|
||||||
let mut values = vec![];
|
|
||||||
let mut found_key = false;
|
|
||||||
let mut partial_value = None;
|
|
||||||
|
|
||||||
// This can iterate forwards because we need to iterate over the whole
|
|
||||||
// section anyways.
|
|
||||||
for event in self.0 {
|
|
||||||
match event {
|
|
||||||
Event::Key(event_key) if event_key == key => found_key = true,
|
|
||||||
Event::Value(v) if found_key => {
|
|
||||||
found_key = false;
|
|
||||||
// Clones the Cow, doesn't copy underlying value if borrowed
|
|
||||||
values.push(normalize_cow(v.clone()));
|
|
||||||
partial_value = None;
|
|
||||||
}
|
|
||||||
Event::ValueNotDone(v) if found_key => {
|
|
||||||
partial_value = Some((*v).to_vec());
|
|
||||||
}
|
|
||||||
Event::ValueDone(v) if found_key => {
|
|
||||||
found_key = false;
|
|
||||||
partial_value.as_mut().unwrap().extend(&**v);
|
|
||||||
values.push(normalize_cow(Cow::Owned(partial_value.take().unwrap())));
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
values
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Retrieves all values that have the provided key name. This may return
|
|
||||||
/// an empty vec, which implies there was values with the provided key.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the conversion failed.
|
|
||||||
pub fn values_as<T: TryFrom<Cow<'event, [u8]>>>(
|
|
||||||
&self,
|
|
||||||
key: &Key,
|
|
||||||
) -> Result<Vec<T>, GitConfigError<'event>> {
|
|
||||||
self.values(key)
|
|
||||||
.into_iter()
|
|
||||||
.map(T::try_from)
|
|
||||||
.collect::<Result<Vec<T>, _>>()
|
|
||||||
.map_err(|_| GitConfigError::FailedConversion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
|
||||||
enum LookupTreeNode<'a> {
|
|
||||||
Terminal(Vec<SectionId>),
|
|
||||||
NonTerminal(HashMap<Cow<'a, str>, Vec<SectionId>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An intermediate representation of a mutable value obtained from
|
|
||||||
/// [`GitConfig`].
|
|
||||||
///
|
|
||||||
/// This holds a mutable reference to the underlying data structure of
|
|
||||||
/// [`GitConfig`], and thus guarantees through Rust's borrower checker that
|
|
||||||
/// multiple mutable references to [`GitConfig`] cannot be owned at the same
|
|
||||||
/// time.
|
|
||||||
pub struct MutableValue<'borrow, 'lookup, 'event> {
|
|
||||||
section: MutableSection<'borrow, 'event>,
|
|
||||||
key: Key<'lookup>,
|
|
||||||
index: usize,
|
|
||||||
size: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MutableValue<'_, '_, '_> {
|
|
||||||
/// Returns the actual value. This is computed each time this is called, so
|
|
||||||
/// it's best to reuse this value or own it if an allocation is acceptable.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// Returns an error if the lookup failed.
|
|
||||||
#[inline]
|
|
||||||
pub fn get(&self) -> Result<Cow<'_, [u8]>, GitConfigError> {
|
|
||||||
self.section
|
|
||||||
.get(&self.key, self.index, self.index + self.size)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the value to the provided one. This modifies the value such that
|
/// Update the value to the provided one. This modifies the value such that
|
||||||
|
@ -350,18 +127,23 @@ impl MutableValue<'_, '_, '_> {
|
||||||
/// new value.
|
/// new value.
|
||||||
pub fn set_bytes(&mut self, input: Vec<u8>) {
|
pub fn set_bytes(&mut self, input: Vec<u8>) {
|
||||||
if self.size > 0 {
|
if self.size > 0 {
|
||||||
self.section.delete(self.index, self.index + self.size);
|
self.section.drain(self.index..=self.index + self.size);
|
||||||
}
|
}
|
||||||
self.size = 3;
|
self.size = 3;
|
||||||
self.section
|
self.section
|
||||||
.set_value(self.index, Key(Cow::Owned(self.key.to_string())), input);
|
.insert(self.index, Event::Value(Cow::Owned(input)));
|
||||||
|
self.section.insert(self.index, Event::KeyValueSeparator);
|
||||||
|
self.section.insert(
|
||||||
|
self.index,
|
||||||
|
Event::Key(Key(Cow::Owned(self.key.0.to_string()))),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes the value. Does nothing when called multiple times in
|
/// Removes the value. Does nothing when called multiple times in
|
||||||
/// succession.
|
/// succession.
|
||||||
pub fn delete(&mut self) {
|
pub fn delete(&mut self) {
|
||||||
if self.size > 0 {
|
if self.size > 0 {
|
||||||
self.section.delete(self.index, self.index + self.size);
|
self.section.drain(self.index..=self.index + self.size);
|
||||||
self.size = 0;
|
self.size = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,7 +248,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if values.is_empty() {
|
if values.is_empty() {
|
||||||
return Err(GitConfigError::KeyDoesNotExist);
|
return Err(GitConfigError::KeyDoesNotExist(self.key.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(values)
|
Ok(values)
|
||||||
|
@ -518,7 +300,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
|
||||||
offset_index,
|
offset_index,
|
||||||
} = self.indices_and_sizes[index];
|
} = self.indices_and_sizes[index];
|
||||||
MutableMultiValue::set_value_inner(
|
MutableMultiValue::set_value_inner(
|
||||||
&self.key,
|
self.key.to_owned(),
|
||||||
&mut self.offsets,
|
&mut self.offsets,
|
||||||
self.section.get_mut(§ion_id).unwrap(),
|
self.section.get_mut(§ion_id).unwrap(),
|
||||||
section_id,
|
section_id,
|
||||||
|
@ -545,7 +327,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
|
||||||
) in self.indices_and_sizes.iter().zip(input)
|
) in self.indices_and_sizes.iter().zip(input)
|
||||||
{
|
{
|
||||||
Self::set_value_inner(
|
Self::set_value_inner(
|
||||||
&self.key,
|
self.key.to_owned(),
|
||||||
&mut self.offsets,
|
&mut self.offsets,
|
||||||
self.section.get_mut(section_id).unwrap(),
|
self.section.get_mut(section_id).unwrap(),
|
||||||
*section_id,
|
*section_id,
|
||||||
|
@ -572,7 +354,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
|
||||||
} in &self.indices_and_sizes
|
} in &self.indices_and_sizes
|
||||||
{
|
{
|
||||||
Self::set_value_inner(
|
Self::set_value_inner(
|
||||||
&self.key,
|
self.key.to_owned(),
|
||||||
&mut self.offsets,
|
&mut self.offsets,
|
||||||
self.section.get_mut(section_id).unwrap(),
|
self.section.get_mut(section_id).unwrap(),
|
||||||
*section_id,
|
*section_id,
|
||||||
|
@ -595,7 +377,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
|
||||||
} in &self.indices_and_sizes
|
} in &self.indices_and_sizes
|
||||||
{
|
{
|
||||||
Self::set_value_inner(
|
Self::set_value_inner(
|
||||||
&self.key,
|
self.key.to_owned(),
|
||||||
&mut self.offsets,
|
&mut self.offsets,
|
||||||
self.section.get_mut(section_id).unwrap(),
|
self.section.get_mut(section_id).unwrap(),
|
||||||
*section_id,
|
*section_id,
|
||||||
|
@ -606,7 +388,7 @@ impl<'lookup, 'event> MutableMultiValue<'_, 'lookup, 'event> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_value_inner<'a: 'event>(
|
fn set_value_inner<'a: 'event>(
|
||||||
key: &Key<'lookup>,
|
key: Key<'lookup>,
|
||||||
offsets: &mut HashMap<SectionId, Vec<Offset>>,
|
offsets: &mut HashMap<SectionId, Vec<Offset>>,
|
||||||
section: &mut Vec<Event<'event>>,
|
section: &mut Vec<Event<'event>>,
|
||||||
section_id: SectionId,
|
section_id: SectionId,
|
||||||
|
@ -772,7 +554,7 @@ impl<'event> GitConfig<'event> {
|
||||||
/// the conversion is already implemented, but this function is flexible and
|
/// the conversion is already implemented, but this function is flexible and
|
||||||
/// will accept any type that implements [`TryFrom<&[u8]>`][`TryFrom`].
|
/// will accept any type that implements [`TryFrom<&[u8]>`][`TryFrom`].
|
||||||
///
|
///
|
||||||
/// Consider [`Self::multi_value`] if you want to get all values of a
|
/// Consider [`Self::get_multi_value`] if you want to get all values of a
|
||||||
/// multivar instead.
|
/// multivar instead.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -789,9 +571,9 @@ impl<'event> GitConfig<'event> {
|
||||||
/// "#;
|
/// "#;
|
||||||
/// let git_config = GitConfig::try_from(config).unwrap();
|
/// let git_config = GitConfig::try_from(config).unwrap();
|
||||||
/// // You can either use the turbofish to determine the type...
|
/// // You can either use the turbofish to determine the type...
|
||||||
/// let a_value = git_config.value::<Integer>("core", None, "a")?;
|
/// let a_value = git_config.get_value::<Integer>("core", None, "a")?;
|
||||||
/// // ... or explicitly declare the type to avoid the turbofish
|
/// // ... or explicitly declare the type to avoid the turbofish
|
||||||
/// let c_value: Boolean = git_config.value("core", None, "c")?;
|
/// let c_value: Boolean = git_config.get_value("core", None, "c")?;
|
||||||
/// # Ok::<(), GitConfigError>(())
|
/// # Ok::<(), GitConfigError>(())
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -803,7 +585,7 @@ impl<'event> GitConfig<'event> {
|
||||||
///
|
///
|
||||||
/// [`values`]: crate::values
|
/// [`values`]: crate::values
|
||||||
/// [`TryFrom`]: std::convert::TryFrom
|
/// [`TryFrom`]: std::convert::TryFrom
|
||||||
pub fn value<'lookup, T: TryFrom<Cow<'event, [u8]>>>(
|
pub fn get_value<'lookup, T: TryFrom<Cow<'event, [u8]>>>(
|
||||||
&'event self,
|
&'event self,
|
||||||
section_name: &'lookup str,
|
section_name: &'lookup str,
|
||||||
subsection_name: Option<&'lookup str>,
|
subsection_name: Option<&'lookup str>,
|
||||||
|
@ -820,7 +602,7 @@ impl<'event> GitConfig<'event> {
|
||||||
/// the conversion is already implemented, but this function is flexible and
|
/// the conversion is already implemented, but this function is flexible and
|
||||||
/// will accept any type that implements [`TryFrom<&[u8]>`][`TryFrom`].
|
/// will accept any type that implements [`TryFrom<&[u8]>`][`TryFrom`].
|
||||||
///
|
///
|
||||||
/// Consider [`Self::value`] if you want to get a single value
|
/// Consider [`Self::get_value`] if you want to get a single value
|
||||||
/// (following last-one-wins resolution) instead.
|
/// (following last-one-wins resolution) instead.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
|
@ -840,7 +622,7 @@ impl<'event> GitConfig<'event> {
|
||||||
/// "#;
|
/// "#;
|
||||||
/// let git_config = GitConfig::try_from(config).unwrap();
|
/// let git_config = GitConfig::try_from(config).unwrap();
|
||||||
/// // You can either use the turbofish to determine the type...
|
/// // You can either use the turbofish to determine the type...
|
||||||
/// let a_value = git_config.multi_value::<Boolean>("core", None, "a")?;
|
/// let a_value = git_config.get_multi_value::<Boolean>("core", None, "a")?;
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// a_value,
|
/// a_value,
|
||||||
/// vec![
|
/// vec![
|
||||||
|
@ -850,7 +632,7 @@ impl<'event> GitConfig<'event> {
|
||||||
/// ]
|
/// ]
|
||||||
/// );
|
/// );
|
||||||
/// // ... or explicitly declare the type to avoid the turbofish
|
/// // ... or explicitly declare the type to avoid the turbofish
|
||||||
/// let c_value: Vec<Value> = git_config.multi_value("core", None, "c")?;
|
/// let c_value: Vec<Value> = git_config.get_multi_value("core", None, "c")?;
|
||||||
/// assert_eq!(c_value, vec![Value::Other(Cow::Borrowed(b"g"))]);
|
/// assert_eq!(c_value, vec![Value::Other(Cow::Borrowed(b"g"))]);
|
||||||
/// # Ok::<(), GitConfigError>(())
|
/// # Ok::<(), GitConfigError>(())
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -863,7 +645,7 @@ impl<'event> GitConfig<'event> {
|
||||||
///
|
///
|
||||||
/// [`values`]: crate::values
|
/// [`values`]: crate::values
|
||||||
/// [`TryFrom`]: std::convert::TryFrom
|
/// [`TryFrom`]: std::convert::TryFrom
|
||||||
pub fn multi_value<'lookup, T: TryFrom<Cow<'event, [u8]>>>(
|
pub fn get_multi_value<'lookup, T: TryFrom<Cow<'event, [u8]>>>(
|
||||||
&'event self,
|
&'event self,
|
||||||
section_name: &'lookup str,
|
section_name: &'lookup str,
|
||||||
subsection_name: Option<&'lookup str>,
|
subsection_name: Option<&'lookup str>,
|
||||||
|
@ -876,68 +658,6 @@ impl<'event> GitConfig<'event> {
|
||||||
.map_err(|_| GitConfigError::FailedConversion)
|
.map_err(|_| GitConfigError::FailedConversion)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an immutable section reference.
|
|
||||||
pub fn section<'lookup>(
|
|
||||||
&mut self,
|
|
||||||
section_name: &'lookup str,
|
|
||||||
subsection_name: Option<&'lookup str>,
|
|
||||||
) -> Result<Section, GitConfigError<'lookup>> {
|
|
||||||
let section_ids =
|
|
||||||
self.get_section_ids_by_name_and_subname(section_name, subsection_name)?;
|
|
||||||
Ok(Section(
|
|
||||||
self.sections.get(section_ids.last().unwrap()).unwrap(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an mutable section reference.
|
|
||||||
pub fn section_mut<'lookup>(
|
|
||||||
&mut self,
|
|
||||||
section_name: &'lookup str,
|
|
||||||
subsection_name: Option<&'lookup str>,
|
|
||||||
) -> Result<MutableSection<'_, 'event>, GitConfigError<'lookup>> {
|
|
||||||
let section_ids =
|
|
||||||
self.get_section_ids_by_name_and_subname(section_name, subsection_name)?;
|
|
||||||
|
|
||||||
Ok(MutableSection(
|
|
||||||
self.sections.get_mut(section_ids.last().unwrap()).unwrap(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a new section to config. This cannot fail.
|
|
||||||
pub fn new_section(
|
|
||||||
&mut self,
|
|
||||||
section_name: impl Into<Cow<'event, str>>,
|
|
||||||
subsection_name: impl Into<Option<Cow<'event, str>>>,
|
|
||||||
) -> MutableSection<'_, 'event> {
|
|
||||||
self.push_section(
|
|
||||||
Some(SectionHeaderName(section_name.into())),
|
|
||||||
subsection_name.into(),
|
|
||||||
&mut Some(vec![]),
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Removes the section, returning the events it had, if any. If multiple
|
|
||||||
/// sections have the same name, then the last one is returned.
|
|
||||||
pub fn remove_section<'lookup>(
|
|
||||||
&mut self,
|
|
||||||
section_name: &'lookup str,
|
|
||||||
subsection_name: impl Into<Option<&'lookup str>>,
|
|
||||||
) -> Option<Vec<Event>> {
|
|
||||||
let section_ids =
|
|
||||||
self.get_section_ids_by_name_and_subname(section_name, subsection_name.into());
|
|
||||||
let section_ids = section_ids.ok()?.pop()?;
|
|
||||||
self.sections.remove(§ion_ids)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Raw value API
|
|
||||||
///
|
|
||||||
/// These functions are the raw value API. Instead of returning Rust structures,
|
|
||||||
/// these functions return bytes which may or may not be owned. Generally
|
|
||||||
/// speaking, you shouldn't need to use these functions, but are exposed in case
|
|
||||||
/// the higher level functions are deficient.
|
|
||||||
impl<'event> GitConfig<'event> {
|
|
||||||
/// Returns an uninterpreted value given a section, an optional subsection
|
/// Returns an uninterpreted value given a section, an optional subsection
|
||||||
/// and key.
|
/// and key.
|
||||||
///
|
///
|
||||||
|
@ -957,18 +677,42 @@ impl<'event> GitConfig<'event> {
|
||||||
// Note: cannot wrap around the raw_multi_value method because we need
|
// Note: cannot wrap around the raw_multi_value method because we need
|
||||||
// to guarantee that the highest section id is used (so that we follow
|
// to guarantee that the highest section id is used (so that we follow
|
||||||
// the "last one wins" resolution strategy by `git-config`).
|
// the "last one wins" resolution strategy by `git-config`).
|
||||||
|
let section_ids =
|
||||||
|
self.get_section_ids_by_name_and_subname(section_name, subsection_name)?;
|
||||||
let key = Key(key.into());
|
let key = Key(key.into());
|
||||||
for section_id in self
|
|
||||||
.get_section_ids_by_name_and_subname(section_name, subsection_name)?
|
for section_id in section_ids.iter().rev() {
|
||||||
.iter()
|
let mut found_key = false;
|
||||||
.rev()
|
let mut latest_value = None;
|
||||||
{
|
let mut partial_value = None;
|
||||||
if let Some(v) = Section(self.sections.get(section_id).unwrap()).value(&key) {
|
|
||||||
return Ok(v.to_vec().into());
|
// section_id is guaranteed to exist in self.sections, else we have a
|
||||||
|
// violated invariant.
|
||||||
|
for event in self.sections.get(section_id).unwrap() {
|
||||||
|
match event {
|
||||||
|
Event::Key(event_key) if *event_key == key => found_key = true,
|
||||||
|
Event::Value(v) if found_key => {
|
||||||
|
found_key = false;
|
||||||
|
latest_value = Some(Cow::Borrowed(v.borrow()));
|
||||||
|
partial_value = None;
|
||||||
|
}
|
||||||
|
Event::ValueNotDone(v) if found_key => {
|
||||||
|
latest_value = None;
|
||||||
|
partial_value = Some((*v).to_vec());
|
||||||
|
}
|
||||||
|
Event::ValueDone(v) if found_key => {
|
||||||
|
found_key = false;
|
||||||
|
partial_value.as_mut().unwrap().extend(&**v);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(v) = latest_value.or_else(|| partial_value.map(normalize_vec)) {
|
||||||
|
return Ok(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(GitConfigError::KeyDoesNotExist)
|
Err(GitConfigError::KeyDoesNotExist(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a mutable reference to an uninterpreted value given a section,
|
/// Returns a mutable reference to an uninterpreted value given a section,
|
||||||
|
@ -1020,14 +764,14 @@ impl<'event> GitConfig<'event> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(MutableValue {
|
return Ok(MutableValue {
|
||||||
section: MutableSection(self.sections.get_mut(section_id).unwrap()),
|
section: self.sections.get_mut(section_id).unwrap(),
|
||||||
key,
|
key,
|
||||||
size,
|
size,
|
||||||
index,
|
index,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(GitConfigError::KeyDoesNotExist)
|
Err(GitConfigError::KeyDoesNotExist(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns all uninterpreted values given a section, an optional subsection
|
/// Returns all uninterpreted values given a section, an optional subsection
|
||||||
|
@ -1079,16 +823,32 @@ impl<'event> GitConfig<'event> {
|
||||||
let key = Key(key.into());
|
let key = Key(key.into());
|
||||||
let mut values = vec![];
|
let mut values = vec![];
|
||||||
for section_id in self.get_section_ids_by_name_and_subname(section_name, subsection_name)? {
|
for section_id in self.get_section_ids_by_name_and_subname(section_name, subsection_name)? {
|
||||||
values.extend(
|
let mut found_key = false;
|
||||||
Section(self.sections.get(§ion_id).unwrap())
|
let mut partial_value = None;
|
||||||
.values(&key)
|
// section_id is guaranteed to exist in self.sections, else we
|
||||||
.iter()
|
// have a violated invariant.
|
||||||
.map(|v| Cow::Owned(v.to_vec())),
|
for event in self.sections.get(§ion_id).unwrap() {
|
||||||
);
|
match event {
|
||||||
|
Event::Key(event_key) if *event_key == key => found_key = true,
|
||||||
|
Event::Value(v) if found_key => {
|
||||||
|
values.push(normalize_bytes(v));
|
||||||
|
found_key = false;
|
||||||
|
}
|
||||||
|
Event::ValueNotDone(v) if found_key => {
|
||||||
|
partial_value = Some((*v).to_vec());
|
||||||
|
}
|
||||||
|
Event::ValueDone(v) if found_key => {
|
||||||
|
found_key = false;
|
||||||
|
partial_value.as_mut().unwrap().extend(&**v);
|
||||||
|
values.push(normalize_vec(partial_value.take().unwrap()));
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if values.is_empty() {
|
if values.is_empty() {
|
||||||
Err(GitConfigError::KeyDoesNotExist)
|
Err(GitConfigError::KeyDoesNotExist(key))
|
||||||
} else {
|
} else {
|
||||||
Ok(values)
|
Ok(values)
|
||||||
}
|
}
|
||||||
|
@ -1194,7 +954,7 @@ impl<'event> GitConfig<'event> {
|
||||||
entries.sort();
|
entries.sort();
|
||||||
|
|
||||||
if entries.is_empty() {
|
if entries.is_empty() {
|
||||||
Err(GitConfigError::KeyDoesNotExist)
|
Err(GitConfigError::KeyDoesNotExist(key))
|
||||||
} else {
|
} else {
|
||||||
Ok(MutableMultiValue {
|
Ok(MutableMultiValue {
|
||||||
section: &mut self.sections,
|
section: &mut self.sections,
|
||||||
|
@ -1342,17 +1102,43 @@ impl<'event> GitConfig<'event> {
|
||||||
self.get_raw_multi_value_mut(section_name, subsection_name, key)
|
self.get_raw_multi_value_mut(section_name, subsection_name, key)
|
||||||
.map(|mut v| v.set_values(new_values))
|
.map(|mut v| v.set_values(new_values))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new section to config. This cannot fail.
|
||||||
|
pub fn new_empty_section(
|
||||||
|
&mut self,
|
||||||
|
section_name: impl Into<Cow<'event, str>>,
|
||||||
|
subsection_name: impl Into<Option<Cow<'event, str>>>,
|
||||||
|
) {
|
||||||
|
self.push_section(
|
||||||
|
Some(SectionHeaderName(section_name.into())),
|
||||||
|
subsection_name.into(),
|
||||||
|
&mut Some(vec![]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes the section, returning the events it had, if any.
|
||||||
|
pub fn remove_section<'lookup>(
|
||||||
|
&mut self,
|
||||||
|
section_name: &'lookup str,
|
||||||
|
subsection_name: impl Into<Option<&'lookup str>>,
|
||||||
|
) -> Option<Vec<Event>> {
|
||||||
|
let section_ids =
|
||||||
|
self.get_section_ids_by_name_and_subname(section_name, subsection_name.into());
|
||||||
|
let section_ids = section_ids.ok()?.pop()?;
|
||||||
|
|
||||||
|
self.sections.remove(§ion_ids)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Private helper functions
|
/// Private helper functions
|
||||||
impl<'event> GitConfig<'event> {
|
impl<'event> GitConfig<'event> {
|
||||||
/// Adds a new section to the config file.
|
/// Used during initialization.
|
||||||
fn push_section(
|
fn push_section(
|
||||||
&mut self,
|
&mut self,
|
||||||
current_section_name: Option<SectionHeaderName<'event>>,
|
current_section_name: Option<SectionHeaderName<'event>>,
|
||||||
current_subsection_name: Option<Cow<'event, str>>,
|
current_subsection_name: Option<Cow<'event, str>>,
|
||||||
maybe_section: &mut Option<Vec<Event<'event>>>,
|
maybe_section: &mut Option<Vec<Event<'event>>>,
|
||||||
) -> Option<MutableSection<'_, 'event>> {
|
) {
|
||||||
if let Some(section) = maybe_section.take() {
|
if let Some(section) = maybe_section.take() {
|
||||||
let new_section_id = SectionId(self.section_id_counter);
|
let new_section_id = SectionId(self.section_id_counter);
|
||||||
self.sections.insert(new_section_id, section);
|
self.sections.insert(new_section_id, section);
|
||||||
|
@ -1367,7 +1153,10 @@ impl<'event> GitConfig<'event> {
|
||||||
if let LookupTreeNode::NonTerminal(subsection) = node {
|
if let LookupTreeNode::NonTerminal(subsection) = node {
|
||||||
found_node = true;
|
found_node = true;
|
||||||
subsection
|
subsection
|
||||||
// Clones the cow, not the inner borrowed str.
|
// Despite the clone `push_section` is always called
|
||||||
|
// with a Cow::Borrowed, so this is effectively a
|
||||||
|
// copy. This copy might not be necessary, but need
|
||||||
|
// to work around borrowck to figure it out.
|
||||||
.entry(subsection_name.clone())
|
.entry(subsection_name.clone())
|
||||||
.or_default()
|
.or_default()
|
||||||
.push(new_section_id);
|
.push(new_section_id);
|
||||||
|
@ -1393,9 +1182,6 @@ impl<'event> GitConfig<'event> {
|
||||||
}
|
}
|
||||||
self.section_order.push_back(new_section_id);
|
self.section_order.push_back(new_section_id);
|
||||||
self.section_id_counter += 1;
|
self.section_id_counter += 1;
|
||||||
self.sections.get_mut(&new_section_id).map(MutableSection)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2132,7 +1918,7 @@ mod get_raw_value {
|
||||||
let config = GitConfig::try_from("[core]\na=b\nc=d").unwrap();
|
let config = GitConfig::try_from("[core]\na=b\nc=d").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.get_raw_value("core", None, "aaaaaa"),
|
config.get_raw_value("core", None, "aaaaaa"),
|
||||||
Err(GitConfigError::KeyDoesNotExist)
|
Err(GitConfigError::KeyDoesNotExist(Key("aaaaaa".into())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2159,8 +1945,8 @@ mod get_value {
|
||||||
#[test]
|
#[test]
|
||||||
fn single_section() -> Result<(), Box<dyn Error>> {
|
fn single_section() -> Result<(), Box<dyn Error>> {
|
||||||
let config = GitConfig::try_from("[core]\na=b\nc").unwrap();
|
let config = GitConfig::try_from("[core]\na=b\nc").unwrap();
|
||||||
let first_value: Value = config.value("core", None, "a")?;
|
let first_value: Value = config.get_value("core", None, "a")?;
|
||||||
let second_value: Boolean = config.value("core", None, "c")?;
|
let second_value: Boolean = config.get_value("core", None, "c")?;
|
||||||
|
|
||||||
assert_eq!(first_value, Value::Other(Cow::Borrowed(b"b")));
|
assert_eq!(first_value, Value::Other(Cow::Borrowed(b"b")));
|
||||||
assert_eq!(second_value, Boolean::True(TrueVariant::Implicit));
|
assert_eq!(second_value, Boolean::True(TrueVariant::Implicit));
|
||||||
|
@ -2172,7 +1958,7 @@ mod get_value {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod get_raw_multi_value {
|
mod get_raw_multi_value {
|
||||||
use super::{Cow, GitConfig, GitConfigError, TryFrom};
|
use super::{Cow, GitConfig, GitConfigError, TryFrom};
|
||||||
use crate::parser::SectionHeaderName;
|
use crate::parser::{Key, SectionHeaderName};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn single_value_is_identical_to_single_value_query() {
|
fn single_value_is_identical_to_single_value_query() {
|
||||||
|
@ -2230,7 +2016,7 @@ mod get_raw_multi_value {
|
||||||
let config = GitConfig::try_from("[core]\na=b\nc=d").unwrap();
|
let config = GitConfig::try_from("[core]\na=b\nc=d").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
config.get_raw_multi_value("core", None, "aaaaaa"),
|
config.get_raw_multi_value("core", None, "aaaaaa"),
|
||||||
Err(GitConfigError::KeyDoesNotExist)
|
Err(GitConfigError::KeyDoesNotExist(Key("aaaaaa".into())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,17 +20,17 @@ fn get_value_for_all_provided_values() -> Result<(), Box<dyn std::error::Error>>
|
||||||
let file = GitConfig::try_from(config)?;
|
let file = GitConfig::try_from(config)?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Boolean>("core", None, "bool-explicit")?,
|
file.get_value::<Boolean>("core", None, "bool-explicit")?,
|
||||||
Boolean::False(Cow::Borrowed("false"))
|
Boolean::False(Cow::Borrowed("false"))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Boolean>("core", None, "bool-implicit")?,
|
file.get_value::<Boolean>("core", None, "bool-implicit")?,
|
||||||
Boolean::True(TrueVariant::Implicit)
|
Boolean::True(TrueVariant::Implicit)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Integer>("core", None, "integer-no-prefix")?,
|
file.get_value::<Integer>("core", None, "integer-no-prefix")?,
|
||||||
Integer {
|
Integer {
|
||||||
value: 10,
|
value: 10,
|
||||||
suffix: None
|
suffix: None
|
||||||
|
@ -38,7 +38,7 @@ fn get_value_for_all_provided_values() -> Result<(), Box<dyn std::error::Error>>
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Integer>("core", None, "integer-no-prefix")?,
|
file.get_value::<Integer>("core", None, "integer-no-prefix")?,
|
||||||
Integer {
|
Integer {
|
||||||
value: 10,
|
value: 10,
|
||||||
suffix: None
|
suffix: None
|
||||||
|
@ -46,7 +46,7 @@ fn get_value_for_all_provided_values() -> Result<(), Box<dyn std::error::Error>>
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Integer>("core", None, "integer-prefix")?,
|
file.get_value::<Integer>("core", None, "integer-prefix")?,
|
||||||
Integer {
|
Integer {
|
||||||
value: 10,
|
value: 10,
|
||||||
suffix: Some(IntegerSuffix::Gibi),
|
suffix: Some(IntegerSuffix::Gibi),
|
||||||
|
@ -54,7 +54,7 @@ fn get_value_for_all_provided_values() -> Result<(), Box<dyn std::error::Error>>
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Color>("core", None, "color")?,
|
file.get_value::<Color>("core", None, "color")?,
|
||||||
Color {
|
Color {
|
||||||
foreground: Some(ColorValue::BrightGreen),
|
foreground: Some(ColorValue::BrightGreen),
|
||||||
background: Some(ColorValue::Red),
|
background: Some(ColorValue::Red),
|
||||||
|
@ -63,7 +63,7 @@ fn get_value_for_all_provided_values() -> Result<(), Box<dyn std::error::Error>>
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Value>("core", None, "other")?,
|
file.get_value::<Value>("core", None, "other")?,
|
||||||
Value::Other(Cow::Borrowed(b"hello world"))
|
Value::Other(Cow::Borrowed(b"hello world"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -86,12 +86,12 @@ fn get_value_looks_up_all_sections_before_failing() -> Result<(), Box<dyn std::e
|
||||||
|
|
||||||
// Checks that we check the last entry first still
|
// Checks that we check the last entry first still
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Boolean>("core", None, "bool-implicit")?,
|
file.get_value::<Boolean>("core", None, "bool-implicit")?,
|
||||||
Boolean::True(TrueVariant::Implicit)
|
Boolean::True(TrueVariant::Implicit)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Boolean>("core", None, "bool-explicit")?,
|
file.get_value::<Boolean>("core", None, "bool-explicit")?,
|
||||||
Boolean::False(Cow::Borrowed("false"))
|
Boolean::False(Cow::Borrowed("false"))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -102,10 +102,12 @@ fn get_value_looks_up_all_sections_before_failing() -> Result<(), Box<dyn std::e
|
||||||
fn section_names_are_case_insensitive() -> Result<(), Box<dyn std::error::Error>> {
|
fn section_names_are_case_insensitive() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let config = "[core] bool-implicit";
|
let config = "[core] bool-implicit";
|
||||||
let file = GitConfig::try_from(config)?;
|
let file = GitConfig::try_from(config)?;
|
||||||
assert!(file.value::<Boolean>("core", None, "bool-implicit").is_ok());
|
assert!(file
|
||||||
|
.get_value::<Boolean>("core", None, "bool-implicit")
|
||||||
|
.is_ok());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Boolean>("core", None, "bool-implicit"),
|
file.get_value::<Boolean>("core", None, "bool-implicit"),
|
||||||
file.value::<Boolean>("CORE", None, "bool-implicit")
|
file.get_value::<Boolean>("CORE", None, "bool-implicit")
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -117,10 +119,10 @@ fn value_names_are_case_insensitive() -> Result<(), Box<dyn std::error::Error>>
|
||||||
a = true
|
a = true
|
||||||
A = false";
|
A = false";
|
||||||
let file = GitConfig::try_from(config)?;
|
let file = GitConfig::try_from(config)?;
|
||||||
assert_eq!(file.multi_value::<Boolean>("core", None, "a")?.len(), 2);
|
assert_eq!(file.get_multi_value::<Boolean>("core", None, "a")?.len(), 2);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
file.value::<Boolean>("core", None, "a"),
|
file.get_value::<Boolean>("core", None, "a"),
|
||||||
file.value::<Boolean>("core", None, "A")
|
file.get_value::<Boolean>("core", None, "A")
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
Reference in a new issue