gix_config/file/section/
body.rs

1use std::{borrow::Cow, iter::FusedIterator, ops::Range};
2
3use bstr::{BStr, BString, ByteVec};
4
5use crate::{
6    parse::{section::ValueName, Event},
7    value::{normalize, normalize_bstr, normalize_bstring},
8};
9
10/// A opaque type that represents a section body.
11#[derive(PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Debug, Default)]
12pub struct Body<'event>(pub(crate) Vec<Event<'event>>);
13
14/// Access
15impl<'event> Body<'event> {
16    /// Retrieves the last matching value in a section with the given value name, if present.
17    ///
18    /// Note that we consider values without separator `=` non-existing, i.e. `[core]\na` would not exist.
19    /// If that's expected, [Self::value_implicit()] must be used instead.
20    #[must_use]
21    pub fn value(&self, value_name: impl AsRef<str>) -> Option<Cow<'_, BStr>> {
22        self.value_implicit(value_name.as_ref()).flatten()
23    }
24
25    /// Retrieves the last matching value in a section with the given value name, if present, and indicates
26    /// an implicit value with `Some(None)`, and a non-existing one as `None`
27    #[must_use]
28    pub fn value_implicit(&self, value_name: &str) -> Option<Option<Cow<'_, BStr>>> {
29        let key = ValueName::from_str_unchecked(value_name);
30        let (_key_range, range) = self.key_and_value_range_by(&key)?;
31        let range = match range {
32            None => return Some(None),
33            Some(range) => range,
34        };
35        let mut concatenated = BString::default();
36
37        for event in &self.0[range] {
38            match event {
39                Event::Value(v) => {
40                    return Some(Some(normalize_bstr(v.as_ref())));
41                }
42                Event::ValueNotDone(v) => {
43                    concatenated.push_str(v.as_ref());
44                }
45                Event::ValueDone(v) => {
46                    concatenated.push_str(v.as_ref());
47                    return Some(Some(normalize_bstring(concatenated)));
48                }
49                _ => (),
50            }
51        }
52        None
53    }
54
55    /// Retrieves all values that have the provided value name. This may return
56    /// an empty vec, which implies there were no values with the provided key.
57    #[must_use]
58    pub fn values(&self, value_name: &str) -> Vec<Cow<'_, BStr>> {
59        let key = &ValueName::from_str_unchecked(value_name);
60        let mut values = Vec::new();
61        let mut expect_value = false;
62        let mut concatenated_value = BString::default();
63
64        for event in &self.0 {
65            match event {
66                Event::SectionValueName(event_key) if event_key == key => expect_value = true,
67                Event::Value(v) if expect_value => {
68                    expect_value = false;
69                    values.push(normalize_bstr(v.as_ref()));
70                }
71                Event::ValueNotDone(v) if expect_value => {
72                    concatenated_value.push_str(v.as_ref());
73                }
74                Event::ValueDone(v) if expect_value => {
75                    expect_value = false;
76                    concatenated_value.push_str(v.as_ref());
77                    values.push(normalize_bstring(std::mem::take(&mut concatenated_value)));
78                }
79                _ => (),
80            }
81        }
82
83        values
84    }
85
86    /// Returns an iterator visiting all value names in order.
87    pub fn value_names(&self) -> impl Iterator<Item = &ValueName<'event>> {
88        self.0.iter().filter_map(|e| match e {
89            Event::SectionValueName(k) => Some(k),
90            _ => None,
91        })
92    }
93
94    /// Returns true if the section contains the provided value name.
95    #[must_use]
96    pub fn contains_value_name(&self, value_name: &str) -> bool {
97        let key = &ValueName::from_str_unchecked(value_name);
98        self.0.iter().any(|e| {
99            matches!(e,
100                Event::SectionValueName(k) if k == key
101            )
102        })
103    }
104
105    /// Returns the number of values in the section.
106    #[must_use]
107    pub fn num_values(&self) -> usize {
108        self.0
109            .iter()
110            .filter(|e| matches!(e, Event::SectionValueName(_)))
111            .count()
112    }
113
114    /// Returns if the section is empty.
115    /// Note that this may count whitespace, see [`num_values()`][Self::num_values()] for
116    /// another way to determine semantic emptiness.
117    #[must_use]
118    pub fn is_void(&self) -> bool {
119        self.0.is_empty()
120    }
121}
122
123impl Body<'_> {
124    pub(crate) fn as_ref(&self) -> &[Event<'_>] {
125        &self.0
126    }
127
128    /// Returns the range containing the value events for the `value_name`, with value range being `None` if there is
129    /// no key-value separator and only a 'fake' Value event with an empty string in side.
130    /// If the value is not found, `None` is returned.
131    pub(crate) fn key_and_value_range_by(
132        &self,
133        value_name: &ValueName<'_>,
134    ) -> Option<(Range<usize>, Option<Range<usize>>)> {
135        let mut value_range = Range::default();
136        let mut key_start = None;
137        for (i, e) in self.0.iter().enumerate().rev() {
138            match e {
139                Event::SectionValueName(k) => {
140                    if k == value_name {
141                        key_start = Some(i);
142                        break;
143                    }
144                    value_range = Range::default();
145                }
146                Event::Value(_) => {
147                    (value_range.start, value_range.end) = (i, i);
148                }
149                Event::ValueNotDone(_) | Event::ValueDone(_) => {
150                    if value_range.end == 0 {
151                        value_range.end = i;
152                    } else {
153                        value_range.start = i;
154                    }
155                }
156                _ => (),
157            }
158        }
159        key_start.map(|key_start| {
160            // value end needs to be offset by one so that the last value's index
161            // is included in the range
162            #[allow(clippy::range_plus_one)]
163            let value_range = value_range.start..value_range.end + 1;
164            let key_range = key_start..value_range.end;
165            (key_range, (value_range.start != key_start + 1).then_some(value_range))
166        })
167    }
168}
169
170/// An owning iterator of a section body. Created by [`Body::into_iter`], yielding
171/// un-normalized (`key`, `value`) pairs.
172// TODO: tests
173pub struct BodyIter<'event>(std::vec::IntoIter<Event<'event>>);
174
175impl<'event> IntoIterator for Body<'event> {
176    type Item = (ValueName<'event>, Cow<'event, BStr>);
177
178    type IntoIter = BodyIter<'event>;
179
180    fn into_iter(self) -> Self::IntoIter {
181        BodyIter(self.0.into_iter())
182    }
183}
184
185impl<'event> Iterator for BodyIter<'event> {
186    type Item = (ValueName<'event>, Cow<'event, BStr>);
187
188    fn next(&mut self) -> Option<Self::Item> {
189        let mut key = None;
190        let mut partial_value = BString::default();
191        let mut value = None;
192
193        for event in self.0.by_ref() {
194            match event {
195                Event::SectionValueName(k) => key = Some(k),
196                Event::Value(v) => {
197                    value = Some(v);
198                    break;
199                }
200                Event::ValueNotDone(v) => partial_value.push_str(v.as_ref()),
201                Event::ValueDone(v) => {
202                    partial_value.push_str(v.as_ref());
203                    value = Some(partial_value.into());
204                    break;
205                }
206                _ => (),
207            }
208        }
209
210        key.zip(value.map(normalize))
211    }
212}
213
214impl FusedIterator for BodyIter<'_> {}