add into bytes for gitconfig
This commit is contained in:
parent
ebfd15b6d3
commit
6ce2c1d89d
3 changed files with 236 additions and 21 deletions
82
src/file.rs
82
src/file.rs
|
@ -313,7 +313,7 @@ pub struct GitConfig<'a> {
|
|||
/// The list of events that occur before an actual section. Since a
|
||||
/// `git-config` file prohibits global values, this vec is limited to only
|
||||
/// comment, newline, and whitespace events.
|
||||
front_matter_events: Vec<Event<'a>>,
|
||||
frontmatter_events: Vec<Event<'a>>,
|
||||
section_lookup_tree: HashMap<Cow<'a, str>, Vec<LookupTreeNode<'a>>>,
|
||||
/// SectionId to section mapping. The value of this HashMap contains actual
|
||||
/// events.
|
||||
|
@ -365,12 +365,12 @@ impl<'event> GitConfig<'event> {
|
|||
///
|
||||
/// [`values`]: crate::values
|
||||
/// [`TryFrom`]: std::convert::TryFrom
|
||||
pub fn get_value<'b, T: TryFrom<Cow<'event, [u8]>>>(
|
||||
pub fn get_value<'lookup, T: TryFrom<Cow<'event, [u8]>>>(
|
||||
&'event self,
|
||||
section_name: &'b str,
|
||||
subsection_name: Option<&'b str>,
|
||||
key: &'b str,
|
||||
) -> Result<T, GitConfigError<'b>> {
|
||||
section_name: &'lookup str,
|
||||
subsection_name: Option<&'lookup str>,
|
||||
key: &'lookup str,
|
||||
) -> Result<T, GitConfigError<'lookup>> {
|
||||
T::try_from(self.get_raw_value(section_name, subsection_name, key)?)
|
||||
.map_err(|_| GitConfigError::FailedConversion)
|
||||
}
|
||||
|
@ -425,12 +425,12 @@ impl<'event> GitConfig<'event> {
|
|||
///
|
||||
/// [`values`]: crate::values
|
||||
/// [`TryFrom`]: std::convert::TryFrom
|
||||
pub fn get_multi_value<'b, T: TryFrom<Cow<'event, [u8]>>>(
|
||||
pub fn get_multi_value<'lookup, T: TryFrom<Cow<'event, [u8]>>>(
|
||||
&'event self,
|
||||
section_name: &'b str,
|
||||
subsection_name: Option<&'b str>,
|
||||
key: &'b str,
|
||||
) -> Result<Vec<T>, GitConfigError<'b>> {
|
||||
section_name: &'lookup str,
|
||||
subsection_name: Option<&'lookup str>,
|
||||
key: &'lookup str,
|
||||
) -> Result<Vec<T>, GitConfigError<'lookup>> {
|
||||
self.get_raw_multi_value(section_name, subsection_name, key)?
|
||||
.into_iter()
|
||||
.map(T::try_from)
|
||||
|
@ -871,6 +871,35 @@ impl<'event> GitConfig<'event> {
|
|||
self.get_raw_multi_value_mut(section_name, subsection_name, key)
|
||||
.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(section_name.into()),
|
||||
subsection_name.into(),
|
||||
&mut Some(vec![]),
|
||||
)
|
||||
}
|
||||
|
||||
/// Removes the section, returning the events it had, if any.
|
||||
pub fn remove_section(
|
||||
&mut self,
|
||||
section_name: impl Into<Cow<'event, str>>,
|
||||
subsection_name: impl Into<Option<Cow<'event, str>>>,
|
||||
) -> Option<Vec<Event>> {
|
||||
let mut section_ids = self
|
||||
.get_section_ids_by_name_and_subname(
|
||||
§ion_name.into(),
|
||||
subsection_name.into().as_deref(),
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
self.sections.remove(§ion_ids.pop()?)
|
||||
}
|
||||
}
|
||||
|
||||
/// Private helper functions
|
||||
|
@ -990,7 +1019,7 @@ impl<'a> TryFrom<&'a [u8]> for GitConfig<'a> {
|
|||
impl<'a> From<Parser<'a>> for GitConfig<'a> {
|
||||
fn from(parser: Parser<'a>) -> Self {
|
||||
let mut new_self = Self {
|
||||
front_matter_events: vec![],
|
||||
frontmatter_events: vec![],
|
||||
sections: HashMap::new(),
|
||||
section_lookup_tree: HashMap::new(),
|
||||
section_headers: HashMap::new(),
|
||||
|
@ -1035,7 +1064,7 @@ impl<'a> From<Parser<'a>> for GitConfig<'a> {
|
|||
e @ Event::Comment(_) | e @ Event::Newline(_) | e @ Event::Whitespace(_) => {
|
||||
match maybe_section {
|
||||
Some(ref mut section) => section.push(e),
|
||||
None => new_self.front_matter_events.push(e),
|
||||
None => new_self.frontmatter_events.push(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1053,12 +1082,37 @@ impl<'a> From<Parser<'a>> for GitConfig<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<Vec<u8>> for GitConfig<'a> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<Vec<u8>> for &GitConfig<'a> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut value = vec![];
|
||||
|
||||
for events in &self.frontmatter_events {
|
||||
value.extend(events.to_vec());
|
||||
}
|
||||
|
||||
for section_id in &self.section_order {
|
||||
value.extend(self.section_headers.get(section_id).unwrap().to_vec());
|
||||
for event in self.sections.get(section_id).unwrap() {
|
||||
value.extend(event.to_vec());
|
||||
}
|
||||
}
|
||||
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for GitConfig<'_> {
|
||||
/// Note that this is a best-effort attempt at printing a `GitConfig`. 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 {
|
||||
for front_matter in &self.front_matter_events {
|
||||
for front_matter in &self.frontmatter_events {
|
||||
front_matter.fmt(f)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,12 @@ pub enum Event<'a> {
|
|||
KeyValueSeparator,
|
||||
}
|
||||
|
||||
impl Event<'_> {
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Event<'_> {
|
||||
/// Note that this is a best-effort attempt at printing an `Event`. If
|
||||
/// there are non UTF-8 values in your config, this will _NOT_ render
|
||||
|
@ -88,6 +94,30 @@ impl Display for Event<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Event<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
Self::Value(e) | Self::ValueNotDone(e) | Self::ValueDone(e) => e.to_vec(),
|
||||
Self::Comment(e) => e.into(),
|
||||
Self::SectionHeader(e) => e.into(),
|
||||
Self::Key(e) | Self::Newline(e) | Self::Whitespace(e) => e.as_bytes().to_vec(),
|
||||
Self::KeyValueSeparator => vec![b'='],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &Event<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
Event::Value(e) | Event::ValueNotDone(e) | Event::ValueDone(e) => e.to_vec(),
|
||||
Event::Comment(e) => e.into(),
|
||||
Event::SectionHeader(e) => e.into(),
|
||||
Event::Key(e) | Event::Newline(e) | Event::Whitespace(e) => e.as_bytes().to_vec(),
|
||||
Event::KeyValueSeparator => vec![b'='],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed section containing the header and the section events.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
||||
pub struct ParsedSection<'a> {
|
||||
|
@ -107,12 +137,6 @@ impl Display for ParsedSection<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<Event<'a>> for ParsedSectionHeader<'a> {
|
||||
fn into(self) -> Event<'a> {
|
||||
Event::SectionHeader(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed section header, containing a name and optionally a subsection name.
|
||||
///
|
||||
/// Note that section headers must be parsed as valid ASCII, and thus all valid
|
||||
|
@ -132,6 +156,12 @@ pub struct ParsedSectionHeader<'a> {
|
|||
pub subsection_name: Option<Cow<'a, str>>,
|
||||
}
|
||||
|
||||
impl ParsedSectionHeader<'_> {
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ParsedSectionHeader<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "[{}", self.name)?;
|
||||
|
@ -151,6 +181,24 @@ impl Display for ParsedSectionHeader<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for ParsedSectionHeader<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &ParsedSectionHeader<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.to_string().into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<Event<'a>> for ParsedSectionHeader<'a> {
|
||||
fn into(self) -> Event<'a> {
|
||||
Event::SectionHeader(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A parsed comment event containing the comment marker and comment.
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
||||
pub struct ParsedComment<'a> {
|
||||
|
@ -174,6 +222,20 @@ impl Display for ParsedComment<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for ParsedComment<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &ParsedComment<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut values = vec![self.comment_tag as u8];
|
||||
values.extend(self.comment.iter());
|
||||
values
|
||||
}
|
||||
}
|
||||
|
||||
/// A parser error reports the one-indexed line number where the parsing error
|
||||
/// occurred, as well as the last parser node and the remaining data to be
|
||||
/// parsed.
|
||||
|
@ -250,7 +312,7 @@ impl Display for ParserNode {
|
|||
///
|
||||
/// This is parser exposes low-level syntactic events from a `git-config` file.
|
||||
/// Generally speaking, you'll want to use [`GitConfig`] as it wraps
|
||||
/// around the parser to provide a higher-level a[u8]action to a `git-config`
|
||||
/// around the parser to provide a higher-level abstraction to a `git-config`
|
||||
/// file, including querying, modifying, and updating values.
|
||||
///
|
||||
/// This parser guarantees that the events emitted are sufficient to
|
||||
|
|
|
@ -149,6 +149,12 @@ pub enum Value<'a> {
|
|||
Other(Cow<'a, [u8]>),
|
||||
}
|
||||
|
||||
impl Value<'_> {
|
||||
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) {
|
||||
|
@ -207,6 +213,23 @@ impl<'a> From<Cow<'a, [u8]>> for Value<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Value<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &Value<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
Value::Boolean(b) => b.into(),
|
||||
Value::Integer(i) => i.into(),
|
||||
Value::Color(c) => c.into(),
|
||||
Value::Other(o) => o.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo display for value
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
|
@ -237,6 +260,16 @@ pub enum Boolean<'a> {
|
|||
False(Cow<'a, str>),
|
||||
}
|
||||
|
||||
impl Boolean<'_> {
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Boolean<'a> {
|
||||
type Error = ();
|
||||
|
||||
|
@ -319,6 +352,27 @@ impl Into<bool> for Boolean<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> Into<&'a [u8]> for &'b Boolean<'a> {
|
||||
fn into(self) -> &'a [u8] {
|
||||
match self {
|
||||
Boolean::True(t) => t.into(),
|
||||
Boolean::False(f) => f.as_bytes(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Boolean<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &Boolean<'_> {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.to_string().into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for Boolean<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
@ -407,6 +461,15 @@ impl Display for TrueVariant<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b: 'a> Into<&'a [u8]> for &'b TrueVariant<'a> {
|
||||
fn into(self) -> &'a [u8] {
|
||||
match self {
|
||||
TrueVariant::Explicit(e) => e.as_bytes(),
|
||||
TrueVariant::Implicit => &[],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl Serialize for TrueVariant<'_> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
|
@ -436,6 +499,12 @@ pub struct Integer {
|
|||
pub suffix: Option<IntegerSuffix>,
|
||||
}
|
||||
|
||||
impl Integer {
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Integer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.value)?;
|
||||
|
@ -516,6 +585,18 @@ impl TryFrom<Cow<'_, [u8]>> for Integer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Integer {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &Integer {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.to_string().into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// Integer prefixes that are supported by `git-config`.
|
||||
///
|
||||
/// These values are base-2 unit of measurements, not the base-10 variants.
|
||||
|
@ -608,6 +689,12 @@ pub struct Color {
|
|||
pub attributes: Vec<ColorAttribute>,
|
||||
}
|
||||
|
||||
impl Color {
|
||||
pub fn to_vec(&self) -> Vec<u8> {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Color {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if let Some(fg) = self.foreground {
|
||||
|
@ -715,6 +802,18 @@ impl TryFrom<Cow<'_, [u8]>> for Color {
|
|||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Color {
|
||||
fn into(self) -> Vec<u8> {
|
||||
(&self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for &Color {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.to_string().into_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
/// Discriminating enum for [`Color`] values.
|
||||
///
|
||||
/// `git-config` supports the eight standard colors, their bright variants, an
|
||||
|
|
Reference in a new issue