gix_config/file/mutable/
mod.rs1use std::borrow::Cow;
2
3use bstr::{BStr, BString, ByteSlice, ByteVec};
4
5use crate::{file, parse::Event};
6
7pub(crate) mod multi_value;
8pub(crate) mod section;
9pub(crate) mod value;
10
11fn escape_value(value: &BStr) -> BString {
12 let starts_with_whitespace = value.first().is_some_and(u8::is_ascii_whitespace);
13 let ends_with_whitespace = value
14 .get(value.len().saturating_sub(1))
15 .is_some_and(u8::is_ascii_whitespace);
16 let contains_comment_indicators = value.find_byteset(b";#").is_some();
17 let quote = starts_with_whitespace || ends_with_whitespace || contains_comment_indicators;
18
19 let mut buf: BString = Vec::with_capacity(value.len()).into();
20 if quote {
21 buf.push(b'"');
22 }
23
24 for b in value.iter().copied() {
25 match b {
26 b'\n' => buf.push_str(r"\n"),
27 b'\t' => buf.push_str(r"\t"),
28 b'"' => buf.push_str(r#"\""#),
29 b'\\' => buf.push_str(r"\\"),
30 _ => buf.push(b),
31 }
32 }
33
34 if quote {
35 buf.push(b'"');
36 }
37 buf
38}
39
40#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
41struct Whitespace<'a> {
42 pre_key: Option<Cow<'a, BStr>>,
43 pre_sep: Option<Cow<'a, BStr>>,
44 post_sep: Option<Cow<'a, BStr>>,
45}
46
47impl Default for Whitespace<'_> {
48 fn default() -> Self {
49 Whitespace {
50 pre_key: Some(b"\t".as_bstr().into()),
51 pre_sep: Some(b" ".as_bstr().into()),
52 post_sep: Some(b" ".as_bstr().into()),
53 }
54 }
55}
56
57impl<'a> Whitespace<'a> {
58 fn key_value_separators(&self) -> Vec<Event<'a>> {
59 let mut out = Vec::with_capacity(3);
60 if let Some(ws) = &self.pre_sep {
61 out.push(Event::Whitespace(ws.clone()));
62 }
63 out.push(Event::KeyValueSeparator);
64 if let Some(ws) = &self.post_sep {
65 out.push(Event::Whitespace(ws.clone()));
66 }
67 out
68 }
69
70 fn from_body(s: &file::section::Body<'a>) -> Self {
71 let key_pos =
72 s.0.iter()
73 .enumerate()
74 .find_map(|(idx, e)| matches!(e, Event::SectionValueName(_)).then(|| idx));
75 key_pos
76 .map(|key_pos| {
77 let pre_key = s.0[..key_pos].iter().next_back().and_then(|e| match e {
78 Event::Whitespace(s) => Some(s.clone()),
79 _ => None,
80 });
81 let from_key = &s.0[key_pos..];
82 let (pre_sep, post_sep) = from_key
83 .iter()
84 .enumerate()
85 .find_map(|(idx, e)| matches!(e, Event::KeyValueSeparator).then(|| idx))
86 .map(|sep_pos| {
87 (
88 from_key.get(sep_pos - 1).and_then(|e| match e {
89 Event::Whitespace(ws) => Some(ws.clone()),
90 _ => None,
91 }),
92 from_key.get(sep_pos + 1).and_then(|e| match e {
93 Event::Whitespace(ws) => Some(ws.clone()),
94 _ => None,
95 }),
96 )
97 })
98 .unwrap_or_default();
99 Whitespace {
100 pre_key,
101 pre_sep,
102 post_sep,
103 }
104 })
105 .unwrap_or_default()
106 }
107}