gix_config/file/access/raw.rs
1use std::{borrow::Cow, collections::HashMap};
2
3use bstr::BStr;
4use smallvec::ToSmallVec;
5
6use crate::{
7 file::{mutable::multi_value::EntryData, Index, Metadata, MultiValueMut, Size, ValueMut},
8 lookup,
9 parse::{section, Event},
10 AsKey, File,
11};
12
13/// # Raw value API
14///
15/// These functions are the raw value API, returning normalized byte strings.
16impl<'event> File<'event> {
17 /// Returns an uninterpreted value given a `key`.
18 ///
19 /// Consider [`Self::raw_values()`] if you want to get all values of
20 /// a multivar instead.
21 pub fn raw_value(&self, key: impl AsKey) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
22 let key = key.as_key();
23 self.raw_value_filter_by(key.section_name, key.subsection_name, key.value_name, |_| true)
24 }
25
26 /// Returns an uninterpreted value given a section, an optional subsection
27 /// and value name.
28 ///
29 /// Consider [`Self::raw_values()`] if you want to get all values of
30 /// a multivar instead.
31 pub fn raw_value_by(
32 &self,
33 section_name: impl AsRef<str>,
34 subsection_name: Option<&BStr>,
35 value_name: impl AsRef<str>,
36 ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
37 self.raw_value_filter_by(section_name, subsection_name, value_name, |_| true)
38 }
39
40 /// Returns an uninterpreted value given a `key`, if it passes the `filter`.
41 ///
42 /// Consider [`Self::raw_values()`] if you want to get all values of
43 /// a multivar instead.
44 pub fn raw_value_filter(
45 &self,
46 key: impl AsKey,
47 filter: impl FnMut(&Metadata) -> bool,
48 ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
49 let key = key.as_key();
50 self.raw_value_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
51 }
52
53 /// Returns an uninterpreted value given a section, an optional subsection
54 /// and value name, if it passes the `filter`.
55 ///
56 /// Consider [`Self::raw_values()`] if you want to get all values of
57 /// a multivar instead.
58 pub fn raw_value_filter_by(
59 &self,
60 section_name: impl AsRef<str>,
61 subsection_name: Option<&BStr>,
62 value_name: impl AsRef<str>,
63 filter: impl FnMut(&Metadata) -> bool,
64 ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
65 self.raw_value_filter_inner(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
66 }
67
68 fn raw_value_filter_inner(
69 &self,
70 section_name: &str,
71 subsection_name: Option<&BStr>,
72 value_name: &str,
73 mut filter: impl FnMut(&Metadata) -> bool,
74 ) -> Result<Cow<'_, BStr>, lookup::existing::Error> {
75 let section_ids = self.section_ids_by_name_and_subname(section_name, subsection_name)?;
76 for section_id in section_ids.rev() {
77 let section = self.sections.get(§ion_id).expect("known section id");
78 if !filter(section.meta()) {
79 continue;
80 }
81 if let Some(v) = section.value(value_name) {
82 return Ok(v);
83 }
84 }
85
86 Err(lookup::existing::Error::KeyMissing)
87 }
88
89 /// Returns a mutable reference to an uninterpreted value given a section,
90 /// an optional subsection and value name.
91 ///
92 /// Consider [`Self::raw_values_mut`] if you want to get mutable
93 /// references to all values of a multivar instead.
94 pub fn raw_value_mut<'lookup>(
95 &mut self,
96 key: &'lookup impl AsKey,
97 ) -> Result<ValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
98 let key = key.as_key();
99 self.raw_value_mut_by(key.section_name, key.subsection_name, key.value_name)
100 }
101
102 /// Returns a mutable reference to an uninterpreted value given a section,
103 /// an optional subsection and value name.
104 ///
105 /// Consider [`Self::raw_values_mut_by`] if you want to get mutable
106 /// references to all values of a multivar instead.
107 pub fn raw_value_mut_by<'lookup>(
108 &mut self,
109 section_name: impl AsRef<str>,
110 subsection_name: Option<&'lookup BStr>,
111 value_name: &'lookup str,
112 ) -> Result<ValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
113 self.raw_value_mut_filter(section_name, subsection_name, value_name, |_| true)
114 }
115
116 /// Returns a mutable reference to an uninterpreted value given a section,
117 /// an optional subsection and value name, and if it passes `filter`.
118 ///
119 /// Consider [`Self::raw_values_mut_by`] if you want to get mutable
120 /// references to all values of a multivar instead.
121 pub fn raw_value_mut_filter<'lookup>(
122 &mut self,
123 section_name: impl AsRef<str>,
124 subsection_name: Option<&'lookup BStr>,
125 value_name: &'lookup str,
126 filter: impl FnMut(&Metadata) -> bool,
127 ) -> Result<ValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
128 self.raw_value_mut_filter_inner(section_name.as_ref(), subsection_name, value_name, filter)
129 }
130
131 fn raw_value_mut_filter_inner<'lookup>(
132 &mut self,
133 section_name: &str,
134 subsection_name: Option<&'lookup BStr>,
135 value_name: &'lookup str,
136 mut filter: impl FnMut(&Metadata) -> bool,
137 ) -> Result<ValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
138 let mut section_ids = self
139 .section_ids_by_name_and_subname(section_name, subsection_name)?
140 .rev();
141 let key = section::ValueName(Cow::<BStr>::Borrowed(value_name.into()));
142
143 while let Some(section_id) = section_ids.next() {
144 let mut index = 0;
145 let mut size = 0;
146 let mut found_key = false;
147 let section = self.sections.get(§ion_id).expect("known section id");
148 if !filter(section.meta()) {
149 continue;
150 }
151 for (i, event) in section.as_ref().iter().enumerate() {
152 match event {
153 Event::SectionValueName(event_key) if *event_key == key => {
154 found_key = true;
155 index = i;
156 size = 1;
157 }
158 Event::Newline(_) | Event::Whitespace(_) | Event::ValueNotDone(_) if found_key => {
159 size += 1;
160 }
161 Event::ValueDone(_) | Event::Value(_) if found_key => {
162 found_key = false;
163 size += 1;
164 }
165 Event::KeyValueSeparator if found_key => {
166 size += 1;
167 }
168 _ => {}
169 }
170 }
171
172 if size == 0 {
173 continue;
174 }
175
176 drop(section_ids);
177 let nl = self.detect_newline_style().to_smallvec();
178 return Ok(ValueMut {
179 section: self.sections.get_mut(§ion_id).expect("known section-id").to_mut(nl),
180 key,
181 index: Index(index),
182 size: Size(size),
183 });
184 }
185
186 Err(lookup::existing::Error::KeyMissing)
187 }
188
189 /// Returns all uninterpreted values given a `key`.
190 ///
191 /// The ordering means that the last of the returned values is the one that would be the
192 /// value used in the single-value case.
193 ///
194 /// # Examples
195 ///
196 /// If you have the following config:
197 ///
198 /// ```text
199 /// [core]
200 /// a = b
201 /// [core]
202 /// a = c
203 /// a = d
204 /// ```
205 ///
206 /// Attempting to get all values of `a` yields the following:
207 ///
208 /// ```
209 /// # use gix_config::File;
210 /// # use std::borrow::Cow;
211 /// # use std::convert::TryFrom;
212 /// # use bstr::BStr;
213 /// # let git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
214 /// assert_eq!(
215 /// git_config.raw_values("core.a").unwrap(),
216 /// vec![
217 /// Cow::<BStr>::Borrowed("b".into()),
218 /// Cow::<BStr>::Borrowed("c".into()),
219 /// Cow::<BStr>::Borrowed("d".into()),
220 /// ],
221 /// );
222 /// ```
223 ///
224 /// Consider [`Self::raw_value`] if you want to get the resolved single
225 /// value for a given key, if your value does not support multi-valued values.
226 pub fn raw_values(&self, key: impl AsKey) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
227 let key = key.as_key();
228 self.raw_values_by(key.section_name, key.subsection_name, key.value_name)
229 }
230
231 /// Returns all uninterpreted values given a section, an optional subsection
232 /// and value name in order of occurrence.
233 ///
234 /// The ordering means that the last of the returned values is the one that would be the
235 /// value used in the single-value case.
236 ///
237 /// # Examples
238 ///
239 /// If you have the following config:
240 ///
241 /// ```text
242 /// [core]
243 /// a = b
244 /// [core]
245 /// a = c
246 /// a = d
247 /// ```
248 ///
249 /// Attempting to get all values of `a` yields the following:
250 ///
251 /// ```
252 /// # use gix_config::File;
253 /// # use std::borrow::Cow;
254 /// # use std::convert::TryFrom;
255 /// # use bstr::BStr;
256 /// # let git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
257 /// assert_eq!(
258 /// git_config.raw_values_by("core", None, "a").unwrap(),
259 /// vec![
260 /// Cow::<BStr>::Borrowed("b".into()),
261 /// Cow::<BStr>::Borrowed("c".into()),
262 /// Cow::<BStr>::Borrowed("d".into()),
263 /// ],
264 /// );
265 /// ```
266 ///
267 /// Consider [`Self::raw_value`] if you want to get the resolved single
268 /// value for a given value name, if your value does not support multi-valued values.
269 pub fn raw_values_by(
270 &self,
271 section_name: impl AsRef<str>,
272 subsection_name: Option<&BStr>,
273 value_name: impl AsRef<str>,
274 ) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
275 self.raw_values_filter_by(section_name, subsection_name, value_name, |_| true)
276 }
277
278 /// Returns all uninterpreted values given a `key`, if the value passes `filter`, in order of occurrence.
279 ///
280 /// The ordering means that the last of the returned values is the one that would be the
281 /// value used in the single-value case.
282 pub fn raw_values_filter(
283 &self,
284 key: impl AsKey,
285 filter: impl FnMut(&Metadata) -> bool,
286 ) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
287 let key = key.as_key();
288 self.raw_values_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
289 }
290
291 /// Returns all uninterpreted values given a section, an optional subsection
292 /// and value name, if the value passes `filter`, in order of occurrence.
293 ///
294 /// The ordering means that the last of the returned values is the one that would be the
295 /// value used in the single-value case.
296 pub fn raw_values_filter_by(
297 &self,
298 section_name: impl AsRef<str>,
299 subsection_name: Option<&BStr>,
300 value_name: impl AsRef<str>,
301 filter: impl FnMut(&Metadata) -> bool,
302 ) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
303 self.raw_values_filter_inner(section_name.as_ref(), subsection_name, value_name.as_ref(), filter)
304 }
305
306 fn raw_values_filter_inner(
307 &self,
308 section_name: &str,
309 subsection_name: Option<&BStr>,
310 value_name: &str,
311 mut filter: impl FnMut(&Metadata) -> bool,
312 ) -> Result<Vec<Cow<'_, BStr>>, lookup::existing::Error> {
313 let mut values = Vec::new();
314 let section_ids = self.section_ids_by_name_and_subname(section_name, subsection_name)?;
315 for section_id in section_ids {
316 let section = self.sections.get(§ion_id).expect("known section id");
317 if !filter(section.meta()) {
318 continue;
319 }
320 values.extend(section.values(value_name));
321 }
322
323 if values.is_empty() {
324 Err(lookup::existing::Error::KeyMissing)
325 } else {
326 Ok(values)
327 }
328 }
329
330 /// Returns mutable references to all uninterpreted values given a `key`.
331 ///
332 /// # Examples
333 ///
334 /// If you have the following config:
335 ///
336 /// ```text
337 /// [core]
338 /// a = b
339 /// [core]
340 /// a = c
341 /// a = d
342 /// ```
343 ///
344 /// Attempting to get all values of `a` yields the following:
345 ///
346 /// ```
347 /// # use gix_config::File;
348 /// # use std::borrow::Cow;
349 /// # use std::convert::TryFrom;
350 /// # use bstr::BStr;
351 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
352 /// assert_eq!(
353 /// git_config.raw_values("core.a")?,
354 /// vec![
355 /// Cow::<BStr>::Borrowed("b".into()),
356 /// Cow::<BStr>::Borrowed("c".into()),
357 /// Cow::<BStr>::Borrowed("d".into())
358 /// ]
359 /// );
360 ///
361 /// git_config.raw_values_mut(&"core.a")?.set_all("g");
362 ///
363 /// assert_eq!(
364 /// git_config.raw_values("core.a")?,
365 /// vec![
366 /// Cow::<BStr>::Borrowed("g".into()),
367 /// Cow::<BStr>::Borrowed("g".into()),
368 /// Cow::<BStr>::Borrowed("g".into())
369 /// ],
370 /// );
371 /// # Ok::<(), gix_config::lookup::existing::Error>(())
372 /// ```
373 ///
374 /// Consider [`Self::raw_value`] if you want to get the resolved single
375 /// value for a given value name, if your value does not support multi-valued values.
376 ///
377 /// Note that this operation is relatively expensive, requiring a full
378 /// traversal of the config.
379 pub fn raw_values_mut<'lookup>(
380 &mut self,
381 key: &'lookup impl AsKey,
382 ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
383 let key = key.as_key();
384 self.raw_values_mut_by(key.section_name, key.subsection_name, key.value_name)
385 }
386
387 /// Returns mutable references to all uninterpreted values given a section,
388 /// an optional subsection and value name.
389 ///
390 /// # Examples
391 ///
392 /// If you have the following config:
393 ///
394 /// ```text
395 /// [core]
396 /// a = b
397 /// [core]
398 /// a = c
399 /// a = d
400 /// ```
401 ///
402 /// Attempting to get all values of `a` yields the following:
403 ///
404 /// ```
405 /// # use gix_config::File;
406 /// # use std::borrow::Cow;
407 /// # use std::convert::TryFrom;
408 /// # use bstr::BStr;
409 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
410 /// assert_eq!(
411 /// git_config.raw_values("core.a")?,
412 /// vec![
413 /// Cow::<BStr>::Borrowed("b".into()),
414 /// Cow::<BStr>::Borrowed("c".into()),
415 /// Cow::<BStr>::Borrowed("d".into())
416 /// ]
417 /// );
418 ///
419 /// git_config.raw_values_mut_by("core", None, "a")?.set_all("g");
420 ///
421 /// assert_eq!(
422 /// git_config.raw_values("core.a")?,
423 /// vec![
424 /// Cow::<BStr>::Borrowed("g".into()),
425 /// Cow::<BStr>::Borrowed("g".into()),
426 /// Cow::<BStr>::Borrowed("g".into())
427 /// ],
428 /// );
429 /// # Ok::<(), gix_config::lookup::existing::Error>(())
430 /// ```
431 ///
432 /// Consider [`Self::raw_value`] if you want to get the resolved single
433 /// value for a given value name, if your value does not support multi-valued values.
434 ///
435 /// Note that this operation is relatively expensive, requiring a full
436 /// traversal of the config.
437 pub fn raw_values_mut_by<'lookup>(
438 &mut self,
439 section_name: impl AsRef<str>,
440 subsection_name: Option<&'lookup BStr>,
441 value_name: &'lookup str,
442 ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
443 self.raw_values_mut_filter_by(section_name, subsection_name, value_name, |_| true)
444 }
445
446 /// Returns mutable references to all uninterpreted values given a `key`,
447 /// if their sections pass `filter`.
448 pub fn raw_values_mut_filter<'lookup>(
449 &mut self,
450 key: &'lookup impl AsKey,
451 filter: impl FnMut(&Metadata) -> bool,
452 ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
453 let key = key.as_key();
454 self.raw_values_mut_filter_by(key.section_name, key.subsection_name, key.value_name, filter)
455 }
456
457 /// Returns mutable references to all uninterpreted values given a section,
458 /// an optional subsection and value name, if their sections pass `filter`.
459 pub fn raw_values_mut_filter_by<'lookup>(
460 &mut self,
461 section_name: impl AsRef<str>,
462 subsection_name: Option<&'lookup BStr>,
463 value_name: &'lookup str,
464 filter: impl FnMut(&Metadata) -> bool,
465 ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
466 self.raw_values_mut_filter_inner(section_name.as_ref(), subsection_name, value_name, filter)
467 }
468
469 fn raw_values_mut_filter_inner<'lookup>(
470 &mut self,
471 section_name: &str,
472 subsection_name: Option<&'lookup BStr>,
473 value_name: &'lookup str,
474 mut filter: impl FnMut(&Metadata) -> bool,
475 ) -> Result<MultiValueMut<'_, 'lookup, 'event>, lookup::existing::Error> {
476 let section_ids = self.section_ids_by_name_and_subname(section_name, subsection_name)?;
477 let key = section::ValueName(Cow::<BStr>::Borrowed(value_name.into()));
478
479 let mut offsets = HashMap::new();
480 let mut entries = Vec::new();
481 for section_id in section_ids.rev() {
482 let mut last_boundary = 0;
483 let mut expect_value = false;
484 let mut offset_list = Vec::new();
485 let mut offset_index = 0;
486 let section = self.sections.get(§ion_id).expect("known section-id");
487 if !filter(section.meta()) {
488 continue;
489 }
490 for (i, event) in section.as_ref().iter().enumerate() {
491 match event {
492 Event::SectionValueName(event_key) if *event_key == key => {
493 expect_value = true;
494 offset_list.push(i - last_boundary);
495 offset_index += 1;
496 last_boundary = i;
497 }
498 Event::Value(_) | Event::ValueDone(_) if expect_value => {
499 expect_value = false;
500 entries.push(EntryData {
501 section_id,
502 offset_index,
503 });
504 offset_list.push(i - last_boundary + 1);
505 offset_index += 1;
506 last_boundary = i + 1;
507 }
508 _ => (),
509 }
510 }
511 offsets.insert(section_id, offset_list);
512 }
513
514 entries.sort();
515
516 if entries.is_empty() {
517 Err(lookup::existing::Error::KeyMissing)
518 } else {
519 Ok(MultiValueMut {
520 section: &mut self.sections,
521 key,
522 indices_and_sizes: entries,
523 offsets,
524 })
525 }
526 }
527
528 /// Sets a value in a given `key`.
529 /// Note that the parts leading to the value name must exist for this method to work, i.e. the
530 /// section and the subsection, if present.
531 ///
532 /// # Examples
533 ///
534 /// Given the config,
535 ///
536 /// ```text
537 /// [core]
538 /// a = b
539 /// [core]
540 /// a = c
541 /// a = d
542 /// ```
543 ///
544 /// Setting a new value to the key `core.a` will yield the following:
545 ///
546 /// ```
547 /// # use gix_config::File;
548 /// # use std::borrow::Cow;
549 /// # use bstr::BStr;
550 /// # use std::convert::TryFrom;
551 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
552 /// git_config.set_existing_raw_value(&"core.a", "e")?;
553 /// assert_eq!(git_config.raw_value("core.a")?, Cow::<BStr>::Borrowed("e".into()));
554 /// assert_eq!(
555 /// git_config.raw_values("core.a")?,
556 /// vec![
557 /// Cow::<BStr>::Borrowed("b".into()),
558 /// Cow::<BStr>::Borrowed("c".into()),
559 /// Cow::<BStr>::Borrowed("e".into())
560 /// ],
561 /// );
562 /// # Ok::<(), Box<dyn std::error::Error>>(())
563 /// ```
564 pub fn set_existing_raw_value<'b>(
565 &mut self,
566 key: &'b impl AsKey,
567 new_value: impl Into<&'b BStr>,
568 ) -> Result<(), lookup::existing::Error> {
569 let key = key.as_key();
570 self.raw_value_mut_by(key.section_name, key.subsection_name, key.value_name)
571 .map(|mut entry| entry.set(new_value))
572 }
573
574 /// Sets a value in a given `section_name`, optional `subsection_name`, and `value_name`.
575 /// Note sections named `section_name` and `subsection_name` (if not `None`)
576 /// must exist for this method to work.
577 ///
578 /// # Examples
579 ///
580 /// Given the config,
581 ///
582 /// ```text
583 /// [core]
584 /// a = b
585 /// [core]
586 /// a = c
587 /// a = d
588 /// ```
589 ///
590 /// Setting a new value to the key `core.a` will yield the following:
591 ///
592 /// ```
593 /// # use gix_config::File;
594 /// # use std::borrow::Cow;
595 /// # use bstr::BStr;
596 /// # use std::convert::TryFrom;
597 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
598 /// git_config.set_existing_raw_value_by("core", None, "a", "e")?;
599 /// assert_eq!(git_config.raw_value("core.a")?, Cow::<BStr>::Borrowed("e".into()));
600 /// assert_eq!(
601 /// git_config.raw_values("core.a")?,
602 /// vec![
603 /// Cow::<BStr>::Borrowed("b".into()),
604 /// Cow::<BStr>::Borrowed("c".into()),
605 /// Cow::<BStr>::Borrowed("e".into())
606 /// ],
607 /// );
608 /// # Ok::<(), Box<dyn std::error::Error>>(())
609 /// ```
610 pub fn set_existing_raw_value_by<'b>(
611 &mut self,
612 section_name: impl AsRef<str>,
613 subsection_name: Option<&BStr>,
614 value_name: impl AsRef<str>,
615 new_value: impl Into<&'b BStr>,
616 ) -> Result<(), lookup::existing::Error> {
617 self.raw_value_mut_by(section_name, subsection_name, value_name.as_ref())
618 .map(|mut entry| entry.set(new_value))
619 }
620
621 /// Sets a value in a given `key`.
622 /// Creates the section if necessary and the value as well, or overwrites the last existing value otherwise.
623 ///
624 /// # Examples
625 ///
626 /// Given the config,
627 ///
628 /// ```text
629 /// [core]
630 /// a = b
631 /// ```
632 ///
633 /// Setting a new value to the key `core.a` will yield the following:
634 ///
635 /// ```
636 /// # use gix_config::File;
637 /// # use std::borrow::Cow;
638 /// # use bstr::BStr;
639 /// # use std::convert::TryFrom;
640 /// # let mut git_config = gix_config::File::try_from("[core]a=b").unwrap();
641 /// let prev = git_config.set_raw_value(&"core.a", "e")?;
642 /// git_config.set_raw_value(&"core.b", "f")?;
643 /// assert_eq!(prev.expect("present").as_ref(), "b");
644 /// assert_eq!(git_config.raw_value("core.a")?, Cow::<BStr>::Borrowed("e".into()));
645 /// assert_eq!(git_config.raw_value("core.b")?, Cow::<BStr>::Borrowed("f".into()));
646 /// # Ok::<(), Box<dyn std::error::Error>>(())
647 /// ```
648 pub fn set_raw_value<'b>(
649 &mut self,
650 key: &'event impl AsKey,
651 new_value: impl Into<&'b BStr>,
652 ) -> Result<Option<Cow<'event, BStr>>, crate::file::set_raw_value::Error> {
653 let key = key.as_key();
654 self.set_raw_value_by(key.section_name, key.subsection_name, key.value_name, new_value)
655 }
656
657 /// Sets a value in a given `section_name`, optional `subsection_name`, and `value_name`.
658 /// Creates the section if necessary and the value as well, or overwrites the last existing value otherwise.
659 ///
660 /// # Examples
661 ///
662 /// Given the config,
663 ///
664 /// ```text
665 /// [core]
666 /// a = b
667 /// ```
668 ///
669 /// Setting a new value to the key `core.a` will yield the following:
670 ///
671 /// ```
672 /// # use gix_config::File;
673 /// # use std::borrow::Cow;
674 /// # use bstr::BStr;
675 /// # use std::convert::TryFrom;
676 /// # let mut git_config = gix_config::File::try_from("[core]a=b").unwrap();
677 /// let prev = git_config.set_raw_value_by("core", None, "a", "e")?;
678 /// git_config.set_raw_value_by("core", None, "b", "f")?;
679 /// assert_eq!(prev.expect("present").as_ref(), "b");
680 /// assert_eq!(git_config.raw_value("core.a")?, Cow::<BStr>::Borrowed("e".into()));
681 /// assert_eq!(git_config.raw_value("core.b")?, Cow::<BStr>::Borrowed("f".into()));
682 /// # Ok::<(), Box<dyn std::error::Error>>(())
683 /// ```
684 pub fn set_raw_value_by<'b, Key, E>(
685 &mut self,
686 section_name: impl AsRef<str>,
687 subsection_name: Option<&BStr>,
688 value_name: Key,
689 new_value: impl Into<&'b BStr>,
690 ) -> Result<Option<Cow<'event, BStr>>, crate::file::set_raw_value::Error>
691 where
692 Key: TryInto<section::ValueName<'event>, Error = E>,
693 section::value_name::Error: From<E>,
694 {
695 self.set_raw_value_filter_by(section_name, subsection_name, value_name, new_value, |_| true)
696 }
697
698 /// Similar to [`set_raw_value()`](Self::set_raw_value()), but only sets existing values in sections matching
699 /// `filter`, creating a new section otherwise.
700 pub fn set_raw_value_filter<'b>(
701 &mut self,
702 key: &'event impl AsKey,
703 new_value: impl Into<&'b BStr>,
704 filter: impl FnMut(&Metadata) -> bool,
705 ) -> Result<Option<Cow<'event, BStr>>, crate::file::set_raw_value::Error> {
706 let key = key.as_key();
707 self.set_raw_value_filter_by(key.section_name, key.subsection_name, key.value_name, new_value, filter)
708 }
709
710 /// Similar to [`set_raw_value_by()`](Self::set_raw_value_by()), but only sets existing values in sections matching
711 /// `filter`, creating a new section otherwise.
712 pub fn set_raw_value_filter_by<'b, Key, E>(
713 &mut self,
714 section_name: impl AsRef<str>,
715 subsection_name: Option<&BStr>,
716 key: Key,
717 new_value: impl Into<&'b BStr>,
718 filter: impl FnMut(&Metadata) -> bool,
719 ) -> Result<Option<Cow<'event, BStr>>, crate::file::set_raw_value::Error>
720 where
721 Key: TryInto<section::ValueName<'event>, Error = E>,
722 section::value_name::Error: From<E>,
723 {
724 let mut section = self.section_mut_or_create_new_filter(section_name, subsection_name, filter)?;
725 Ok(section.set(
726 key.try_into().map_err(section::value_name::Error::from)?,
727 new_value.into(),
728 ))
729 }
730
731 /// Sets a multivar in a given `key`.
732 ///
733 /// This internally zips together the new values and the existing values.
734 /// As a result, if more new values are provided than the current amount of
735 /// multivars, then the latter values are not applied. If there are less
736 /// new values than old ones then the remaining old values are unmodified.
737 ///
738 /// **Note**: Mutation order is _not_ guaranteed and is non-deterministic.
739 /// If you need finer control over which values of the multivar are set,
740 /// consider using [`raw_values_mut()`](Self::raw_values_mut()), which will let you iterate
741 /// and check over the values instead. This is best used as a convenience
742 /// function for setting multivars whose values should be treated as an
743 /// unordered set.
744 ///
745 /// # Examples
746 ///
747 /// Let us use the follow config for all examples:
748 ///
749 /// ```text
750 /// [core]
751 /// a = b
752 /// [core]
753 /// a = c
754 /// a = d
755 /// ```
756 ///
757 /// Setting an equal number of values:
758 ///
759 /// ```
760 /// # use gix_config::File;
761 /// # use std::borrow::Cow;
762 /// # use std::convert::TryFrom;
763 /// # use bstr::BStr;
764 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
765 /// let new_values = vec![
766 /// "x",
767 /// "y",
768 /// "z",
769 /// ];
770 /// git_config.set_existing_raw_multi_value(&"core.a", new_values.into_iter())?;
771 /// let fetched_config = git_config.raw_values("core.a")?;
772 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));
773 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));
774 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("z".into())));
775 /// # Ok::<(), gix_config::lookup::existing::Error>(())
776 /// ```
777 ///
778 /// Setting less than the number of present values sets the first ones found:
779 ///
780 /// ```
781 /// # use gix_config::File;
782 /// # use std::borrow::Cow;
783 /// # use std::convert::TryFrom;
784 /// # use bstr::BStr;
785 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
786 /// let new_values = vec![
787 /// "x",
788 /// "y",
789 /// ];
790 /// git_config.set_existing_raw_multi_value(&"core.a", new_values.into_iter())?;
791 /// let fetched_config = git_config.raw_values("core.a")?;
792 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));
793 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));
794 /// # Ok::<(), gix_config::lookup::existing::Error>(())
795 /// ```
796 ///
797 /// Setting more than the number of present values discards the rest:
798 ///
799 /// ```
800 /// # use gix_config::File;
801 /// # use std::borrow::Cow;
802 /// # use std::convert::TryFrom;
803 /// # use bstr::BStr;
804 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
805 /// let new_values = vec![
806 /// "x",
807 /// "y",
808 /// "z",
809 /// "discarded",
810 /// ];
811 /// git_config.set_existing_raw_multi_value(&"core.a", new_values)?;
812 /// assert!(!git_config.raw_values("core.a")?.contains(&Cow::<BStr>::Borrowed("discarded".into())));
813 /// # Ok::<(), gix_config::lookup::existing::Error>(())
814 /// ```
815 pub fn set_existing_raw_multi_value<'a, Iter, Item>(
816 &mut self,
817 key: &'a impl AsKey,
818 new_values: Iter,
819 ) -> Result<(), lookup::existing::Error>
820 where
821 Iter: IntoIterator<Item = Item>,
822 Item: Into<&'a BStr>,
823 {
824 let key = key.as_key();
825 self.set_existing_raw_multi_value_by(key.section_name, key.subsection_name, key.value_name, new_values)
826 }
827
828 /// Sets a multivar in a given section, optional subsection, and key value.
829 ///
830 /// This internally zips together the new values and the existing values.
831 /// As a result, if more new values are provided than the current amount of
832 /// multivars, then the latter values are not applied. If there are less
833 /// new values than old ones then the remaining old values are unmodified.
834 ///
835 /// **Note**: Mutation order is _not_ guaranteed and is non-deterministic.
836 /// If you need finer control over which values of the multivar are set,
837 /// consider using [`raw_values_mut()`](Self::raw_values_mut()), which will let you iterate
838 /// and check over the values instead. This is best used as a convenience
839 /// function for setting multivars whose values should be treated as an
840 /// unordered set.
841 ///
842 /// # Examples
843 ///
844 /// Let us use the follow config for all examples:
845 ///
846 /// ```text
847 /// [core]
848 /// a = b
849 /// [core]
850 /// a = c
851 /// a = d
852 /// ```
853 ///
854 /// Setting an equal number of values:
855 ///
856 /// ```
857 /// # use gix_config::File;
858 /// # use std::borrow::Cow;
859 /// # use std::convert::TryFrom;
860 /// # use bstr::BStr;
861 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
862 /// let new_values = vec![
863 /// "x",
864 /// "y",
865 /// "z",
866 /// ];
867 /// git_config.set_existing_raw_multi_value_by("core", None, "a", new_values.into_iter())?;
868 /// let fetched_config = git_config.raw_values("core.a")?;
869 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));
870 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));
871 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("z".into())));
872 /// # Ok::<(), gix_config::lookup::existing::Error>(())
873 /// ```
874 ///
875 /// Setting less than the number of present values sets the first ones found:
876 ///
877 /// ```
878 /// # use gix_config::File;
879 /// # use std::borrow::Cow;
880 /// # use std::convert::TryFrom;
881 /// # use bstr::BStr;
882 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
883 /// let new_values = vec![
884 /// "x",
885 /// "y",
886 /// ];
887 /// git_config.set_existing_raw_multi_value_by("core", None, "a", new_values.into_iter())?;
888 /// let fetched_config = git_config.raw_values("core.a")?;
889 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("x".into())));
890 /// assert!(fetched_config.contains(&Cow::<BStr>::Borrowed("y".into())));
891 /// # Ok::<(), gix_config::lookup::existing::Error>(())
892 /// ```
893 ///
894 /// Setting more than the number of present values discards the rest:
895 ///
896 /// ```
897 /// # use gix_config::File;
898 /// # use std::borrow::Cow;
899 /// # use std::convert::TryFrom;
900 /// # use bstr::BStr;
901 /// # let mut git_config = gix_config::File::try_from("[core]a=b\n[core]\na=c\na=d").unwrap();
902 /// let new_values = vec![
903 /// "x",
904 /// "y",
905 /// "z",
906 /// "discarded",
907 /// ];
908 /// git_config.set_existing_raw_multi_value_by("core", None, "a", new_values)?;
909 /// assert!(!git_config.raw_values("core.a")?.contains(&Cow::<BStr>::Borrowed("discarded".into())));
910 /// # Ok::<(), gix_config::lookup::existing::Error>(())
911 /// ```
912 pub fn set_existing_raw_multi_value_by<'a, Iter, Item>(
913 &mut self,
914 section_name: impl AsRef<str>,
915 subsection_name: Option<&BStr>,
916 value_name: impl AsRef<str>,
917 new_values: Iter,
918 ) -> Result<(), lookup::existing::Error>
919 where
920 Iter: IntoIterator<Item = Item>,
921 Item: Into<&'a BStr>,
922 {
923 self.raw_values_mut_by(section_name, subsection_name, value_name.as_ref())
924 .map(|mut v| v.set_values(new_values))
925 }
926}