gix_config/file/mutable/
section.rs

1use std::{
2    borrow::Cow,
3    ops::{Deref, Range},
4};
5
6use bstr::{BStr, BString, ByteSlice, ByteVec};
7use gix_sec::Trust;
8use smallvec::SmallVec;
9
10use crate::{
11    file::{
12        self,
13        mutable::{escape_value, Whitespace},
14        Index, Section, Size,
15    },
16    lookup, parse,
17    parse::{section::ValueName, Event},
18    value::{normalize, normalize_bstr, normalize_bstring},
19};
20
21/// A opaque type that represents a mutable reference to a section.
22#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
23pub struct SectionMut<'a, 'event> {
24    section: &'a mut Section<'event>,
25    implicit_newline: bool,
26    whitespace: Whitespace<'event>,
27    newline: SmallVec<[u8; 2]>,
28}
29
30/// Mutating methods.
31impl<'event> SectionMut<'_, 'event> {
32    /// Adds an entry to the end of this section name `value_name` and `value`. If `value` is `None`, no equal sign will be written leaving
33    /// just the key. This is useful for boolean values which are true if merely the key exists.
34    pub fn push<'b>(&mut self, value_name: ValueName<'event>, value: Option<&'b BStr>) -> &mut Self {
35        self.push_with_comment_inner(value_name, value, None);
36        self
37    }
38
39    /// Adds an entry to the end of this section name `value_name` and `value`. If `value` is `None`, no equal sign will be written leaving
40    /// just the key. This is useful for boolean values which are true if merely the key exists.
41    /// `comment` has to be the text to put right after the value and behind a `#` character. Note that newlines are silently transformed
42    /// into spaces.
43    pub fn push_with_comment<'b, 'c>(
44        &mut self,
45        value_name: ValueName<'event>,
46        value: Option<&'b BStr>,
47        comment: impl Into<&'c BStr>,
48    ) -> &mut Self {
49        self.push_with_comment_inner(value_name, value, comment.into().into());
50        self
51    }
52
53    fn push_with_comment_inner(&mut self, value_name: ValueName<'event>, value: Option<&BStr>, comment: Option<&BStr>) {
54        let body = &mut self.section.body.0;
55        if let Some(ws) = &self.whitespace.pre_key {
56            body.push(Event::Whitespace(ws.clone()));
57        }
58
59        body.push(Event::SectionValueName(value_name));
60        match value {
61            Some(value) => {
62                body.extend(self.whitespace.key_value_separators());
63                body.push(Event::Value(escape_value(value).into()));
64            }
65            None => body.push(Event::Value(Cow::Borrowed("".into()))),
66        }
67        if let Some(comment) = comment {
68            body.push(Event::Whitespace(Cow::Borrowed(" ".into())));
69            body.push(Event::Comment(parse::Comment {
70                tag: b'#',
71                text: Cow::Owned({
72                    let mut c = Vec::with_capacity(comment.len());
73                    let mut bytes = comment.iter().peekable();
74                    if !bytes.peek().map_or(true, |b| b.is_ascii_whitespace()) {
75                        c.insert(0, b' ');
76                    }
77                    c.extend(bytes.map(|b| if *b == b'\n' { b' ' } else { *b }));
78                    c.into()
79                }),
80            }));
81        }
82        if self.implicit_newline {
83            body.push(Event::Newline(BString::from(self.newline.to_vec()).into()));
84        }
85    }
86
87    /// Removes all events until a key value pair is removed. This will also
88    /// remove the whitespace preceding the key value pair, if any is found.
89    pub fn pop(&mut self) -> Option<(ValueName<'_>, Cow<'event, BStr>)> {
90        let mut values = Vec::new();
91        // events are popped in reverse order
92        let body = &mut self.section.body.0;
93        while let Some(e) = body.pop() {
94            match e {
95                Event::SectionValueName(k) => {
96                    // pop leading whitespace
97                    if let Some(Event::Whitespace(_)) = body.last() {
98                        body.pop();
99                    }
100
101                    if values.len() == 1 {
102                        let value = values.pop().expect("vec is non-empty but popped to empty value");
103                        return Some((k, normalize(value)));
104                    }
105
106                    return Some((
107                        k,
108                        normalize_bstring({
109                            let mut s = BString::default();
110                            for value in values.into_iter().rev() {
111                                s.push_str(value.as_ref());
112                            }
113                            s
114                        }),
115                    ));
116                }
117                Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) => values.push(v),
118                _ => (),
119            }
120        }
121        None
122    }
123
124    /// Sets the last key value pair if it exists, or adds the new value.
125    /// Returns the previous value if it replaced a value, or None if it adds
126    /// the value.
127    pub fn set(&mut self, value_name: ValueName<'event>, value: &BStr) -> Option<Cow<'event, BStr>> {
128        match self.key_and_value_range_by(&value_name) {
129            None => {
130                self.push(value_name, Some(value));
131                None
132            }
133            Some((key_range, value_range)) => {
134                let value_range = value_range.unwrap_or(key_range.end - 1..key_range.end);
135                let range_start = value_range.start;
136                let ret = self.remove_internal(value_range, false);
137                self.section
138                    .body
139                    .0
140                    .insert(range_start, Event::Value(escape_value(value).into()));
141                Some(ret)
142            }
143        }
144    }
145
146    /// Set the trust level in the meta-data of this section to `trust`.
147    pub fn set_trust(&mut self, trust: Trust) -> &mut Self {
148        let mut meta = (*self.section.meta).clone();
149        meta.trust = trust;
150        self.section.meta = meta.into();
151        self
152    }
153
154    /// Removes the latest value by key and returns it, if it exists.
155    pub fn remove(&mut self, value_name: &str) -> Option<Cow<'event, BStr>> {
156        let key = ValueName::from_str_unchecked(value_name);
157        let (key_range, _value_range) = self.key_and_value_range_by(&key)?;
158        Some(self.remove_internal(key_range, true))
159    }
160
161    /// Adds a new line event. Note that you don't need to call this unless
162    /// you've disabled implicit newlines.
163    pub fn push_newline(&mut self) -> &mut Self {
164        self.section
165            .body
166            .0
167            .push(Event::Newline(Cow::Owned(BString::from(self.newline.to_vec()))));
168        self
169    }
170
171    /// Return the newline used when calling [`push_newline()`][Self::push_newline()].
172    pub fn newline(&self) -> &BStr {
173        self.newline.as_slice().as_bstr()
174    }
175
176    /// Enables or disables automatically adding newline events after adding
177    /// a value. This is _enabled by default_.
178    pub fn set_implicit_newline(&mut self, on: bool) -> &mut Self {
179        self.implicit_newline = on;
180        self
181    }
182
183    /// Sets the exact whitespace to use before each newly created key-value pair,
184    /// with only whitespace characters being permissible.
185    ///
186    /// The default is 2 tabs.
187    /// Set to `None` to disable adding whitespace before a key value.
188    ///
189    /// # Panics
190    ///
191    /// If non-whitespace characters are used. This makes the method only suitable for validated
192    /// or known input.
193    pub fn set_leading_whitespace(&mut self, whitespace: Option<Cow<'event, BStr>>) -> &mut Self {
194        assert!(
195            whitespace
196                .as_deref()
197                .map_or(true, |ws| ws.iter().all(u8::is_ascii_whitespace)),
198            "input whitespace must only contain whitespace characters."
199        );
200        self.whitespace.pre_key = whitespace;
201        self
202    }
203
204    /// Returns the whitespace this section will insert before the
205    /// beginning of a key, if any.
206    #[must_use]
207    pub fn leading_whitespace(&self) -> Option<&BStr> {
208        self.whitespace.pre_key.as_deref()
209    }
210
211    /// Returns the whitespace to be used before and after the `=` between the key
212    /// and the value.
213    ///
214    /// For example, `k = v` will have `(Some(" "), Some(" "))`, whereas `k=\tv` will
215    /// have `(None, Some("\t"))`.
216    #[must_use]
217    pub fn separator_whitespace(&self) -> (Option<&BStr>, Option<&BStr>) {
218        (self.whitespace.pre_sep.as_deref(), self.whitespace.post_sep.as_deref())
219    }
220}
221
222// Internal methods that may require exact indices for faster operations.
223impl<'a, 'event> SectionMut<'a, 'event> {
224    pub(crate) fn new(section: &'a mut Section<'event>, newline: SmallVec<[u8; 2]>) -> Self {
225        let whitespace = Whitespace::from_body(&section.body);
226        Self {
227            section,
228            implicit_newline: true,
229            whitespace,
230            newline,
231        }
232    }
233
234    pub(crate) fn get(
235        &self,
236        key: &ValueName<'_>,
237        start: Index,
238        end: Index,
239    ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
240        let mut expect_value = false;
241        let mut concatenated_value = BString::default();
242
243        for event in &self.section.0[start.0..end.0] {
244            match event {
245                Event::SectionValueName(event_key) if event_key == key => expect_value = true,
246                Event::Value(v) if expect_value => return Ok(normalize_bstr(v.as_ref())),
247                Event::ValueNotDone(v) if expect_value => {
248                    concatenated_value.push_str(v.as_ref());
249                }
250                Event::ValueDone(v) if expect_value => {
251                    concatenated_value.push_str(v.as_ref());
252                    return Ok(normalize_bstring(concatenated_value));
253                }
254                _ => (),
255            }
256        }
257
258        Err(lookup::existing::Error::KeyMissing)
259    }
260
261    pub(crate) fn delete(&mut self, start: Index, end: Index) {
262        self.section.body.0.drain(start.0..end.0);
263    }
264
265    pub(crate) fn set_internal(&mut self, index: Index, key: ValueName<'event>, value: &BStr) -> Size {
266        let mut size = 0;
267
268        let body = &mut self.section.body.0;
269        body.insert(index.0, Event::Value(escape_value(value).into()));
270        size += 1;
271
272        let sep_events = self.whitespace.key_value_separators();
273        size += sep_events.len();
274        body.splice(index.0..index.0, sep_events.into_iter().rev())
275            .for_each(|_| {});
276
277        body.insert(index.0, Event::SectionValueName(key));
278        size += 1;
279
280        Size(size)
281    }
282
283    /// Performs the removal, assuming the range is valid.
284    fn remove_internal(&mut self, range: Range<usize>, fix_whitespace: bool) -> Cow<'event, BStr> {
285        let events = &mut self.section.body.0;
286        if fix_whitespace && events.get(range.end).is_some_and(|ev| matches!(ev, Event::Newline(_))) {
287            events.remove(range.end);
288        }
289        let value = events
290            .drain(range.clone())
291            .fold(Cow::Owned(BString::default()), |mut acc: Cow<'_, BStr>, e| {
292                if let Event::Value(v) | Event::ValueNotDone(v) | Event::ValueDone(v) = e {
293                    acc.to_mut().extend(&**v);
294                }
295                acc
296            });
297        if fix_whitespace
298            && range
299                .start
300                .checked_sub(1)
301                .and_then(|pos| events.get(pos))
302                .is_some_and(|ev| matches!(ev, Event::Whitespace(_)))
303        {
304            events.remove(range.start - 1);
305        }
306        value
307    }
308}
309
310impl<'event> Deref for SectionMut<'_, 'event> {
311    type Target = file::Section<'event>;
312
313    fn deref(&self) -> &Self::Target {
314        self.section
315    }
316}
317
318impl<'event> file::section::Body<'event> {
319    pub(crate) fn as_mut(&mut self) -> &mut Vec<Event<'event>> {
320        &mut self.0
321    }
322}