gix_config/file/section/
body.rs1use 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#[derive(Clone, Debug, Default)]
13pub(crate) struct BodyData(pub(crate) Vec<Event>);
14
15#[derive(Copy, Clone, Debug)]
17pub struct BodyRef<'a> {
18 pub(crate) body: &'a BodyData,
19 pub(crate) backing: &'a [u8],
20}
21
22impl BodyRef<'_> {
24 #[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 #[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 #[must_use]
43 pub fn values(&self, value_name: &str) -> Vec<BString> {
44 self.body.values_in(self.backing, value_name)
45 }
46
47 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 #[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 #[must_use]
68 pub fn num_values(&self) -> usize {
69 self.body.num_values()
70 }
71
72 #[must_use]
76 pub fn is_void(&self) -> bool {
77 self.body.is_void()
78 }
79}
80
81impl 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 #[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 #[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 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 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
234pub 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<'_> {}