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