summaryrefslogtreecommitdiffstats
path: root/vendor/gix-config/src/file/mutable/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-config/src/file/mutable/mod.rs')
-rw-r--r--vendor/gix-config/src/file/mutable/mod.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/vendor/gix-config/src/file/mutable/mod.rs b/vendor/gix-config/src/file/mutable/mod.rs
new file mode 100644
index 000000000..ad99e09b9
--- /dev/null
+++ b/vendor/gix-config/src/file/mutable/mod.rs
@@ -0,0 +1,107 @@
+use std::borrow::Cow;
+
+use bstr::{BStr, BString, ByteSlice, ByteVec};
+
+use crate::{file, parse::Event};
+
+pub(crate) mod multi_value;
+pub(crate) mod section;
+pub(crate) mod value;
+
+fn escape_value(value: &BStr) -> BString {
+ let starts_with_whitespace = value.first().map_or(false, |b| b.is_ascii_whitespace());
+ let ends_with_whitespace = value
+ .get(value.len().saturating_sub(1))
+ .map_or(false, |b| b.is_ascii_whitespace());
+ let contains_comment_indicators = value.find_byteset(b";#").is_some();
+ let quote = starts_with_whitespace || ends_with_whitespace || contains_comment_indicators;
+
+ let mut buf: BString = Vec::with_capacity(value.len()).into();
+ if quote {
+ buf.push(b'"');
+ }
+
+ for b in value.iter().copied() {
+ match b {
+ b'\n' => buf.push_str("\\n"),
+ b'\t' => buf.push_str("\\t"),
+ b'"' => buf.push_str("\\\""),
+ b'\\' => buf.push_str("\\\\"),
+ _ => buf.push(b),
+ }
+ }
+
+ if quote {
+ buf.push(b'"');
+ }
+ buf
+}
+
+#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
+struct Whitespace<'a> {
+ pre_key: Option<Cow<'a, BStr>>,
+ pre_sep: Option<Cow<'a, BStr>>,
+ post_sep: Option<Cow<'a, BStr>>,
+}
+
+impl Default for Whitespace<'_> {
+ fn default() -> Self {
+ Whitespace {
+ pre_key: Some(b"\t".as_bstr().into()),
+ pre_sep: Some(b" ".as_bstr().into()),
+ post_sep: Some(b" ".as_bstr().into()),
+ }
+ }
+}
+
+impl<'a> Whitespace<'a> {
+ fn key_value_separators(&self) -> Vec<Event<'a>> {
+ let mut out = Vec::with_capacity(3);
+ if let Some(ws) = &self.pre_sep {
+ out.push(Event::Whitespace(ws.clone()));
+ }
+ out.push(Event::KeyValueSeparator);
+ if let Some(ws) = &self.post_sep {
+ out.push(Event::Whitespace(ws.clone()));
+ }
+ out
+ }
+
+ fn from_body(s: &file::section::Body<'a>) -> Self {
+ let key_pos =
+ s.0.iter()
+ .enumerate()
+ .find_map(|(idx, e)| matches!(e, Event::SectionKey(_)).then(|| idx));
+ key_pos
+ .map(|key_pos| {
+ let pre_key = s.0[..key_pos].iter().rev().next().and_then(|e| match e {
+ Event::Whitespace(s) => Some(s.clone()),
+ _ => None,
+ });
+ let from_key = &s.0[key_pos..];
+ let (pre_sep, post_sep) = from_key
+ .iter()
+ .enumerate()
+ .find_map(|(idx, e)| matches!(e, Event::KeyValueSeparator).then(|| idx))
+ .map(|sep_pos| {
+ (
+ from_key.get(sep_pos - 1).and_then(|e| match e {
+ Event::Whitespace(ws) => Some(ws.clone()),
+ _ => None,
+ }),
+ from_key.get(sep_pos + 1).and_then(|e| match e {
+ Event::Whitespace(ws) => Some(ws.clone()),
+ _ => None,
+ }),
+ )
+ })
+ .unwrap_or_default();
+ Whitespace {
+ pre_key,
+ pre_sep,
+ post_sep,
+ }
+ })
+ .unwrap_or_default()
+ }
+}