gix_config/file/access/
raw.rs

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