gix_config/file/mutable/
section.rs

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