Skip to main content

gix_config/file/section/
body.rs

1use std::{borrow::Cow, iter::FusedIterator, ops::Range, slice};
2
3use bstr::{BStr, BString, ByteSlice, ByteVec};
4
5use crate::{
6    file::write::extract_newline,
7    parse::{Event, section::ValueName},
8    value::normalize,
9};
10
11/// The span-backed body stored inside a [`File`][crate::File].
12#[derive(Clone, Debug, Default)]
13pub(crate) struct BodyData(pub(crate) Vec<Event>);
14
15/// A view of a section body whose bytes are owned by the containing [`File`][crate::File].
16#[derive(Copy, Clone, Debug)]
17pub struct BodyRef<'a> {
18    pub(crate) body: &'a BodyData,
19    pub(crate) backing: &'a [u8],
20}
21
22/// Access
23impl BodyRef<'_> {
24    /// Retrieves the last matching value in a section with the given value name, if present.
25    ///
26    /// Note that we consider values without separator `=` non-existing, i.e. `[core]\na` would not exist.
27    /// If that's expected, [Self::value_implicit()] must be used instead.
28    #[must_use]
29    pub fn value(&self, value_name: impl AsRef<str>) -> Option<BString> {
30        self.value_implicit(value_name.as_ref()).flatten()
31    }
32
33    /// Retrieves the last matching value in a section with the given value name, if present, and indicates
34    /// an implicit value with `Some(None)`, and a non-existing one as `None`
35    #[must_use]
36    pub fn value_implicit(&self, value_name: &str) -> Option<Option<BString>> {
37        self.body.value_implicit_in(self.backing, value_name)
38    }
39
40    /// Retrieves all values that have the provided value name. This may return
41    /// an empty vec, which implies there were no values with the provided key.
42    #[must_use]
43    pub fn values(&self, value_name: &str) -> Vec<BString> {
44        self.body.values_in(self.backing, value_name)
45    }
46
47    /// Returns an iterator visiting all value names in order.
48    pub fn value_names(&self) -> impl Iterator<Item = String> + '_ {
49        self.body.0.iter().filter_map(move |e| match e {
50            Event::SectionValueName(k) => Some(
51                k.as_bstr_in(self.backing)
52                    .to_str()
53                    .expect("parsed value names are ASCII")
54                    .to_owned(),
55            ),
56            _ => None,
57        })
58    }
59
60    /// Returns true if the section contains the provided value name.
61    #[must_use]
62    pub fn contains_value_name(&self, value_name: &str) -> bool {
63        self.body.contains_value_name_in(self.backing, value_name)
64    }
65
66    /// Returns the number of values in the section.
67    #[must_use]
68    pub fn num_values(&self) -> usize {
69        self.body.num_values()
70    }
71
72    /// Returns if the section is empty.
73    /// Note that this may count whitespace, see [`num_values()`][Self::num_values()] for
74    /// another way to determine semantic emptiness.
75    #[must_use]
76    pub fn is_void(&self) -> bool {
77        self.body.is_void()
78    }
79}
80
81/// Access
82impl BodyData {
83    pub(crate) fn value_implicit_in(&self, backing: &[u8], value_name: &str) -> Option<Option<BString>> {
84        let key = ValueName::from_str_unchecked(value_name);
85        let (_key_range, range) = self.key_and_value_range_by_in(backing, &key)?;
86        let range = match range {
87            None => return Some(None),
88            Some(range) => range,
89        };
90        let mut concatenated = BString::default();
91
92        for event in &self.0[range] {
93            match event {
94                Event::Value(v) => {
95                    return Some(Some(normalize(v.as_slice_in(backing)).into_owned()));
96                }
97                Event::ValueNotDone(v) => {
98                    concatenated.push_str(v.as_slice_in(backing));
99                }
100                Event::ValueDone(v) => {
101                    concatenated.push_str(v.as_slice_in(backing));
102                    return Some(Some(normalize(&concatenated).into_owned()));
103                }
104                _ => (),
105            }
106        }
107        None
108    }
109
110    pub(crate) fn values_in(&self, backing: &[u8], value_name: &str) -> Vec<BString> {
111        let key = &ValueName::from_str_unchecked(value_name);
112        let mut values = Vec::new();
113        let mut expect_value = false;
114        let mut concatenated_value = BString::default();
115
116        for event in &self.0 {
117            match event {
118                Event::SectionValueName(event_key)
119                    if event_key.as_bstr_in(backing).eq_ignore_ascii_case(key.0.as_slice()) =>
120                {
121                    expect_value = true;
122                }
123                Event::Value(v) if expect_value => {
124                    expect_value = false;
125                    values.push(normalize(v.as_slice_in(backing)).into_owned());
126                }
127                Event::ValueNotDone(v) if expect_value => {
128                    concatenated_value.push_str(v.as_slice_in(backing));
129                }
130                Event::ValueDone(v) if expect_value => {
131                    expect_value = false;
132                    concatenated_value.push_str(v.as_slice_in(backing));
133                    let concatenated_value = std::mem::take(&mut concatenated_value);
134                    values.push(normalize(&concatenated_value).into_owned());
135                }
136                _ => (),
137            }
138        }
139
140        values
141    }
142
143    pub(crate) fn contains_value_name_in(&self, backing: &[u8], value_name: &str) -> bool {
144        let key = &ValueName::from_str_unchecked(value_name);
145        self.0.iter().any(|e| {
146            matches!(e,
147                Event::SectionValueName(k) if k.as_bstr_in(backing).eq_ignore_ascii_case(key.0.as_slice())
148            )
149        })
150    }
151
152    /// Returns the number of values in the section.
153    #[must_use]
154    pub fn num_values(&self) -> usize {
155        self.0
156            .iter()
157            .filter(|e| matches!(e, Event::SectionValueName(_)))
158            .count()
159    }
160
161    /// Returns if the section is empty.
162    /// Note that this may count whitespace, see [`num_values()`][Self::num_values()] for
163    /// another way to determine semantic emptiness.
164    #[must_use]
165    pub fn is_void(&self) -> bool {
166        self.0.is_empty()
167    }
168}
169
170impl BodyData {
171    pub(crate) fn as_ref(&self) -> &[Event] {
172        &self.0
173    }
174
175    pub(crate) fn detect_newline_style_in<'a>(&'a self, backing: &'a [u8]) -> Option<&'a BStr> {
176        self.0.iter().find_map(|event| extract_newline(event, backing))
177    }
178
179    /// Returns the range containing the value events for the `value_name`, with value range being `None` if there is
180    /// no key-value separator and only a 'fake' Value event with an empty string in side.
181    /// If the value is not found, `None` is returned.
182    pub(crate) fn key_and_value_range_by_in(
183        &self,
184        backing: &[u8],
185        value_name: &ValueName,
186    ) -> Option<(Range<usize>, Option<Range<usize>>)> {
187        let mut value_range = Range::default();
188        let mut key_start = None;
189        for (i, e) in self.0.iter().enumerate().rev() {
190            match e {
191                Event::SectionValueName(k) => {
192                    if k.as_bstr_in(backing).eq_ignore_ascii_case(value_name.0.as_slice()) {
193                        key_start = Some(i);
194                        break;
195                    }
196                    value_range = Range::default();
197                }
198                Event::Value(_) => {
199                    (value_range.start, value_range.end) = (i, i);
200                }
201                Event::ValueNotDone(_) | Event::ValueDone(_) => {
202                    if value_range.end == 0 {
203                        value_range.end = i;
204                    } else {
205                        value_range.start = i;
206                    }
207                }
208                _ => (),
209            }
210        }
211        key_start.map(|key_start| {
212            // value end needs to be offset by one so that the last value's index
213            // is included in the range
214            let value_range = value_range.start..value_range.end + 1;
215            let key_range = key_start..value_range.end;
216            (key_range, (value_range.start != key_start + 1).then_some(value_range))
217        })
218    }
219
220    pub(crate) fn copy_to_backing_in(
221        &self,
222        source: &[u8],
223        target: &mut Vec<u8>,
224    ) -> Result<Self, crate::parse::span::Error> {
225        Ok(BodyData(
226            self.0
227                .iter()
228                .map(|event| event.copy_to_backing_in(source, target))
229                .collect::<Result<_, _>>()?,
230        ))
231    }
232}
233
234/// An iterator over a section body view, yielding un-normalized (`key`, `value`) pairs.
235pub struct BodyRefIter<'a> {
236    iter: slice::Iter<'a, Event>,
237    backing: &'a [u8],
238}
239
240impl<'a> IntoIterator for BodyRef<'a> {
241    type Item = (String, BString);
242
243    type IntoIter = BodyRefIter<'a>;
244
245    fn into_iter(self) -> Self::IntoIter {
246        BodyRefIter {
247            iter: self.body.0.iter(),
248            backing: self.backing,
249        }
250    }
251}
252
253impl Iterator for BodyRefIter<'_> {
254    type Item = (String, BString);
255
256    fn next(&mut self) -> Option<Self::Item> {
257        let mut key = None;
258        let mut partial_value = BString::default();
259        let mut value = None;
260
261        for event in self.iter.by_ref() {
262            match event {
263                Event::SectionValueName(k) => {
264                    key = Some(
265                        k.as_bstr_in(self.backing)
266                            .to_str()
267                            .expect("parsed value names are ASCII")
268                            .to_owned(),
269                    );
270                }
271                Event::Value(v) => {
272                    value = Some(Cow::Borrowed(v.as_bstr_in(self.backing)));
273                    break;
274                }
275                Event::ValueNotDone(v) => partial_value.push_str(v.as_slice_in(self.backing)),
276                Event::ValueDone(v) => {
277                    partial_value.push_str(v.as_slice_in(self.backing));
278                    value = Some(Cow::Owned(partial_value));
279                    break;
280                }
281                _ => (),
282            }
283        }
284
285        key.zip(value.map(|value| normalize(value.as_ref()).into_owned()))
286    }
287}
288
289impl FusedIterator for BodyRefIter<'_> {}