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 @@
1+use bstr::BStr;
12 use std::{
23 borrow::Cow,
34 collections::{HashMap, VecDeque},
@@ -441,6 +442,42 @@ impl<'event> GitConfig<'event> { · 437 unmodified lines
441442 T::try_from(self.raw_value(section_name, subsection_name, key)?).map_err(lookup::Error::FailedConversion)
442443 }
443444
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+
444481 /// Returns all interpreted values given a section, an optional subsection
445482 /// and key.
446483 ///
git-config/src/values.rs+7−7
@@ -2,7 +2,7 @@ · 1 unmodified line
22
33 use std::{borrow::Cow, convert::TryFrom, fmt::Display, str::FromStr};
44
5−use bstr::BStr;
5+use bstr::{BStr, BString};
66 #[cfg(feature = "serde")]
77 use serde::{Serialize, Serializer};
88
@@ -377,9 +377,9 @@ impl Boolean<'_> { · 368 unmodified lines
377377 /// The error returned when creating `Boolean` from byte string.
378378 #[derive(Debug, PartialEq, thiserror::Error)]
379379 #[allow(missing_docs)]
380−pub enum BooleanError {
381− #[error("Invalid argument format")]
382− InvalidFormat,
380+#[error("Invalid boolean value: '{}'", .input)]
381+pub struct BooleanError {
382+ pub input: BString,
383383 }
384384
385385 impl<'a> TryFrom<&'a [u8]> for Boolean<'a> {
@@ -401,7 +401,7 @@ impl<'a> TryFrom<&'a [u8]> for Boolean<'a> { · 15 unmodified lines
401401 ));
402402 }
403403
404− Err(BooleanError::InvalidFormat)
404+ Err(BooleanError { input: value.into() })
405405 }
406406 }
407407
@@ -512,7 +512,7 @@ impl<'a> TryFrom<&'a [u8]> for TrueVariant<'a> { · 104 unmodified lines
512512 } else if value.is_empty() {
513513 Ok(Self::Implicit)
514514 } else {
515− Err(BooleanError::InvalidFormat)
515+ Err(BooleanError { input: value.into() })
516516 }
517517 }
518518 }
@@ -532,7 +532,7 @@ impl TryFrom<Vec<u8>> for TrueVariant<'_> { · 13 unmodified lines
532532 } else if value.is_empty() {
533533 Ok(Self::Implicit)
534534 } else {
535− Err(BooleanError::InvalidFormat)
535+ Err(BooleanError { input: value.into() })
536536 }
537537 }
538538 }
git-config/tests/value/mod.rs+14−0
@@ -23,11 +23,20 @@ fn get_value_for_all_provided_values() -> crate::Result { · 22 unmodified lines
2323 file.value::<Boolean>("core", None, "bool-explicit")?,
2424 Boolean::False(Cow::Borrowed("false"))
2525 );
26+ assert_eq!(file.boolean("core", None, "bool-explicit").expect("exists")?, false);
2627
2728 assert_eq!(
2829 file.value::<Boolean>("core", None, "bool-implicit")?,
2930 Boolean::True(TrueVariant::Implicit)
3031 );
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);
3140
3241 assert_eq!(
3342 file.value::<Integer>("core", None, "integer-no-prefix")?,
@@ -69,6 +78,11 @@ fn get_value_for_all_provided_values() -> crate::Result { · 35 unmodified lines
6978 }
7079 );
7180
81+ assert_eq!(
82+ file.string("core", None, "other").expect("present").as_ref(),
83+ "hello world"
84+ );
85+
7286 let actual = file.value::<git_config::values::Path>("core", None, "location")?;
7387 assert_eq!(
7488 &*actual,