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(&section_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(&section_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(&section_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(&section_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(&section_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}