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#[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
30impl<'event> SectionMut<'_, 'event> {
32 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 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 pub fn pop(&mut self) -> Option<(ValueName<'_>, Cow<'event, BStr>)> {
90 let mut values = Vec::new();
91 let body = &mut self.section.body.0;
93 while let Some(e) = body.pop() {
94 match e {
95 Event::SectionValueName(k) => {
96 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 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 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 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 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 pub fn newline(&self) -> &BStr {
173 self.newline.as_slice().as_bstr()
174 }
175
176 pub fn set_implicit_newline(&mut self, on: bool) -> &mut Self {
179 self.implicit_newline = on;
180 self
181 }
182
183 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 #[must_use]
207 pub fn leading_whitespace(&self) -> Option<&BStr> {
208 self.whitespace.pre_key.as_deref()
209 }
210
211 #[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
222impl<'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(§ion.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 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}