History

feat: support for `try_value()`, `boolean()` and `string()` access`. (#301)

Support for a convenient way of knowing if a value does or doesn't exist via `try_value()`, which can only fail if the conversion fails. Lastly, `string()` is a special case which doesn't fail as there is no conversion, and `boolean()` allows to obtain a plain boolean value if it was a valid boolean representation.

Sebastian Thiel committed 4 years ago

Commit dc3dc3b1 parent 4496b5a3 files · +58 −7Browse files

Unified · Split

3 files
  1. git-config/src/file/git_config.rs +37 −0
  2. git-config/src/values.rs +7 −7
  3. git-config/tests/value/mod.rs +14 −0
git-config/src/file/git_config.rs+37−0
@@ -1,3 +1,4 @@
1use bstr::BStr;
1use std::{2use std::{
2 borrow::Cow,3 borrow::Cow,
3 collections::{HashMap, VecDeque},4 collections::{HashMap, VecDeque},
@@ -441,6 +442,42 @@ impl<'event> GitConfig<'event> { · 437 unmodified lines
441 T::try_from(self.raw_value(section_name, subsection_name, key)?).map_err(lookup::Error::FailedConversion)442 T::try_from(self.raw_value(section_name, subsection_name, key)?).map_err(lookup::Error::FailedConversion)
442 }443 }
443 444
445 /// Like [`value()`][GitConfig::value()], but returning an `Option` if the value wasn't found.
446 pub fn try_value<T: TryFrom<Cow<'event, [u8]>>>(
447 &'event self,
448 section_name: &str,
449 subsection_name: Option<&str>,
450 key: &str,
451 ) -> Option<Result<T, T::Error>> {
452 self.raw_value(section_name, subsection_name, key).ok().map(T::try_from)
453 }
454
455 /// Like [`value()`][GitConfig::value()], but returning an `Option` if the string wasn't found.
456 ///
457 /// As strings perform no conversions, this will never fail.
458 pub fn string(
459 &'event self,
460 section_name: &str,
461 subsection_name: Option<&str>,
462 key: &str,
463 ) -> Option<Cow<'event, BStr>> {
464 self.raw_value(section_name, subsection_name, key)
465 .ok()
466 .map(|v| values::String::from(v).value)
467 }
468
469 /// Like [`value()`][GitConfig::value()], but returning an `Option` if the boolean wasn't found.
470 pub fn boolean(
471 &'event self,
472 section_name: &str,
473 subsection_name: Option<&str>,
474 key: &str,
475 ) -> Option<Result<bool, values::BooleanError>> {
476 self.raw_value(section_name, subsection_name, key)
477 .ok()
478 .map(|v| values::Boolean::try_from(v).map(|b| b.to_bool()))
479 }
480
444 /// Returns all interpreted values given a section, an optional subsection481 /// Returns all interpreted values given a section, an optional subsection
445 /// and key.482 /// and key.
446 ///483 ///
git-config/src/values.rs+7−7
@@ -2,7 +2,7 @@ · 1 unmodified line
2 2
3use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr};3use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr};
4 4
5use bstr::BStr;5use bstr::{BStr, BString};
6#[cfg(feature = "serde")]6#[cfg(feature = "serde")]
7use serde::{Serialize, Serializer};7use serde::{Serialize, Serializer};
8 8
@@ -377,9 +377,9 @@ impl Boolean<'_> { · 368 unmodified lines
377/// The error returned when creating `Boolean` from byte string.377/// The error returned when creating `Boolean` from byte string.
378#[derive(Debug, PartialEq, thiserror::Error)]378#[derive(Debug, PartialEq, thiserror::Error)]
379#[allow(missing_docs)]379#[allow(missing_docs)]
380pub enum BooleanError {380#[error("Invalid boolean value: '{}'", .input)]
381 #[error("Invalid argument format")]381pub struct BooleanError {
382 InvalidFormat,382 pub input: BString,
383}383}
384 384
385impl<'a> TryFrom<&'a [u8]> for Boolean<'a> {385impl<'a> TryFrom<&'a [u8]> for Boolean<'a> {
@@ -401,7 +401,7 @@ impl<'a> TryFrom<&'a [u8]> for Boolean<'a> { · 15 unmodified lines
401 ));401 ));
402 }402 }
403 403
404 Err(BooleanError::InvalidFormat)404 Err(BooleanError { input: value.into() })
405 }405 }
406}406}
407 407
@@ -512,7 +512,7 @@ impl<'a> TryFrom<&'a [u8]> for TrueVariant<'a> { · 104 unmodified lines
512 } else if value.is_empty() {512 } else if value.is_empty() {
513 Ok(Self::Implicit)513 Ok(Self::Implicit)
514 } else {514 } else {
515 Err(BooleanError::InvalidFormat)515 Err(BooleanError { input: value.into() })
516 }516 }
517 }517 }
518}518}
@@ -532,7 +532,7 @@ impl TryFrom<Vec<u8>> for TrueVariant<'_> { · 13 unmodified lines
532 } else if value.is_empty() {532 } else if value.is_empty() {
533 Ok(Self::Implicit)533 Ok(Self::Implicit)
534 } else {534 } else {
535 Err(BooleanError::InvalidFormat)535 Err(BooleanError { input: value.into() })
536 }536 }
537 }537 }
538}538}
git-config/tests/value/mod.rs+14−0
@@ -23,11 +23,20 @@ fn get_value_for_all_provided_values() -> crate::Result { · 22 unmodified lines
23 file.value::<Boolean>("core", None, "bool-explicit")?,23 file.value::<Boolean>("core", None, "bool-explicit")?,
24 Boolean::False(Cow::Borrowed("false"))24 Boolean::False(Cow::Borrowed("false"))
25 );25 );
26 assert_eq!(file.boolean("core", None, "bool-explicit").expect("exists")?, false);
26 27
27 assert_eq!(28 assert_eq!(
28 file.value::<Boolean>("core", None, "bool-implicit")?,29 file.value::<Boolean>("core", None, "bool-implicit")?,
29 Boolean::True(TrueVariant::Implicit)30 Boolean::True(TrueVariant::Implicit)
30 );31 );
32 assert_eq!(
33 file.try_value::<Boolean>("core", None, "bool-implicit")
34 .expect("exists")?,
35 Boolean::True(TrueVariant::Implicit)
36 );
37
38 assert_eq!(file.boolean("core", None, "bool-implicit").expect("present")?, true);
39 assert_eq!(file.try_value::<String>("doesnt", None, "exist"), None);
31 40
32 assert_eq!(41 assert_eq!(
33 file.value::<Integer>("core", None, "integer-no-prefix")?,42 file.value::<Integer>("core", None, "integer-no-prefix")?,
@@ -69,6 +78,11 @@ fn get_value_for_all_provided_values() -> crate::Result { · 35 unmodified lines
69 }78 }
70 );79 );
71 80
81 assert_eq!(
82 file.string("core", None, "other").expect("present").as_ref(),
83 "hello world"
84 );
85
72 let actual = file.value::<git_config::values::Path>("core", None, "location")?;86 let actual = file.value::<git_config::values::Path>("core", None, "location")?;
73 assert_eq!(87 assert_eq!(
74 &*actual,88 &*actual,