Skip to main content

gix_config/file/access/
raw.rs

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