add into bytes for gitconfig

master
Edward Shen 2021-03-03 20:11:58 -05:00
parent ebfd15b6d3
commit 6ce2c1d89d
Signed by: edward
GPG Key ID: 19182661E818369F
3 changed files with 236 additions and 21 deletions

View File

@ -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(
&section_name.into(),
subsection_name.into().as_deref(),
)
.ok()?;
self.sections.remove(&section_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)?;
}

View File

@ -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

View File

@ -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