Merge pull request #467 from KodrAus/feat/visitor

Add a simple zero-dependency visitor for kv values
This commit is contained in:
Ashley Mannix
2021-11-14 21:07:52 +10:00
committed by GitHub
2 changed files with 274 additions and 0 deletions
+20
View File
@@ -11,6 +11,7 @@ enum Inner {
#[cfg(feature = "std")]
Boxed(std_support::BoxedError),
Msg(&'static str),
Value(value_bag::Error),
Fmt,
}
@@ -21,6 +22,24 @@ impl Error {
inner: Inner::Msg(msg),
}
}
// Not public so we don't leak the `value_bag` API
pub(super) fn from_value(err: value_bag::Error) -> Self {
Error {
inner: Inner::Value(err),
}
}
// Not public so we don't leak the `value_bag` API
pub(super) fn into_value(self) -> value_bag::Error {
match self.inner {
Inner::Value(err) => err,
#[cfg(feature = "kv_unstable_std")]
_ => value_bag::Error::boxed(self),
#[cfg(not(feature = "kv_unstable_std"))]
_ => value_bag::Error::msg("error inspecting a value"),
}
}
}
impl fmt::Display for Error {
@@ -29,6 +48,7 @@ impl fmt::Display for Error {
match &self.inner {
#[cfg(feature = "std")]
&Boxed(ref err) => err.fmt(f),
&Value(ref err) => err.fmt(f),
&Msg(ref msg) => msg.fmt(f),
&Fmt => fmt::Error.fmt(f),
}
+254
View File
@@ -263,6 +263,78 @@ impl<'v> Value<'v> {
pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
self.inner.downcast_ref::<T>()
}
/// Inspect this value using a simple visitor.
pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> {
struct Visitor<V>(V);
impl<'v, V> value_bag::visit::Visit<'v> for Visitor<V>
where
V: Visit<'v>,
{
fn visit_any(&mut self, value: ValueBag) -> Result<(), value_bag::Error> {
self.0
.visit_any(Value { inner: value })
.map_err(Error::into_value)
}
fn visit_u64(&mut self, value: u64) -> Result<(), value_bag::Error> {
self.0.visit_u64(value).map_err(Error::into_value)
}
fn visit_i64(&mut self, value: i64) -> Result<(), value_bag::Error> {
self.0.visit_i64(value).map_err(Error::into_value)
}
fn visit_u128(&mut self, value: u128) -> Result<(), value_bag::Error> {
self.0.visit_u128(value).map_err(Error::into_value)
}
fn visit_i128(&mut self, value: i128) -> Result<(), value_bag::Error> {
self.0.visit_i128(value).map_err(Error::into_value)
}
fn visit_f64(&mut self, value: f64) -> Result<(), value_bag::Error> {
self.0.visit_f64(value).map_err(Error::into_value)
}
fn visit_bool(&mut self, value: bool) -> Result<(), value_bag::Error> {
self.0.visit_bool(value).map_err(Error::into_value)
}
fn visit_str(&mut self, value: &str) -> Result<(), value_bag::Error> {
self.0.visit_str(value).map_err(Error::into_value)
}
fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), value_bag::Error> {
self.0.visit_borrowed_str(value).map_err(Error::into_value)
}
fn visit_char(&mut self, value: char) -> Result<(), value_bag::Error> {
self.0.visit_char(value).map_err(Error::into_value)
}
#[cfg(feature = "kv_unstable_std")]
fn visit_error(
&mut self,
err: &(dyn std::error::Error + 'static),
) -> Result<(), value_bag::Error> {
self.0.visit_error(err).map_err(Error::into_value)
}
#[cfg(feature = "kv_unstable_std")]
fn visit_borrowed_error(
&mut self,
err: &'v (dyn std::error::Error + 'static),
) -> Result<(), value_bag::Error> {
self.0.visit_borrowed_error(err).map_err(Error::into_value)
}
}
self.inner
.visit(&mut Visitor(visitor))
.map_err(Error::from_value)
}
}
impl<'v> fmt::Debug for Value<'v> {
@@ -448,6 +520,142 @@ mod std_support {
self.inner.to_str()
}
}
impl<'v> From<&'v String> for Value<'v> {
fn from(v: &'v String) -> Self {
Value::from(&**v)
}
}
}
/// A visitor for a `Value`.
pub trait Visit<'v> {
/// Visit a `Value`.
///
/// This is the only required method on `Visit` and acts as a fallback for any
/// more specific methods that aren't overridden.
/// The `Value` may be formatted using its `fmt::Debug` or `fmt::Display` implementation,
/// or serialized using its `sval::Value` or `serde::Serialize` implementation.
fn visit_any(&mut self, value: Value) -> Result<(), Error>;
/// Visit an unsigned integer.
fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a signed integer.
fn visit_i64(&mut self, value: i64) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a big unsigned integer.
fn visit_u128(&mut self, value: u128) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a big signed integer.
fn visit_i128(&mut self, value: i128) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a floating point.
fn visit_f64(&mut self, value: f64) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a boolean.
fn visit_bool(&mut self, value: bool) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a string.
fn visit_str(&mut self, value: &str) -> Result<(), Error> {
self.visit_any(value.into())
}
/// Visit a string.
fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> {
self.visit_str(value)
}
/// Visit a Unicode character.
fn visit_char(&mut self, value: char) -> Result<(), Error> {
let mut b = [0; 4];
self.visit_str(&*value.encode_utf8(&mut b))
}
/// Visit an error.
#[cfg(feature = "kv_unstable_std")]
fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> {
self.visit_any(Value::from_dyn_error(err))
}
/// Visit an error.
#[cfg(feature = "kv_unstable_std")]
fn visit_borrowed_error(
&mut self,
err: &'v (dyn std::error::Error + 'static),
) -> Result<(), Error> {
self.visit_any(Value::from_dyn_error(err))
}
}
impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T
where
T: Visit<'v>,
{
fn visit_any(&mut self, value: Value) -> Result<(), Error> {
(**self).visit_any(value)
}
fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
(**self).visit_u64(value)
}
fn visit_i64(&mut self, value: i64) -> Result<(), Error> {
(**self).visit_i64(value)
}
fn visit_u128(&mut self, value: u128) -> Result<(), Error> {
(**self).visit_u128(value)
}
fn visit_i128(&mut self, value: i128) -> Result<(), Error> {
(**self).visit_i128(value)
}
fn visit_f64(&mut self, value: f64) -> Result<(), Error> {
(**self).visit_f64(value)
}
fn visit_bool(&mut self, value: bool) -> Result<(), Error> {
(**self).visit_bool(value)
}
fn visit_str(&mut self, value: &str) -> Result<(), Error> {
(**self).visit_str(value)
}
fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> {
(**self).visit_borrowed_str(value)
}
fn visit_char(&mut self, value: char) -> Result<(), Error> {
(**self).visit_char(value)
}
#[cfg(feature = "kv_unstable_std")]
fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> {
(**self).visit_error(err)
}
#[cfg(feature = "kv_unstable_std")]
fn visit_borrowed_error(
&mut self,
err: &'v (dyn std::error::Error + 'static),
) -> Result<(), Error> {
(**self).visit_borrowed_error(err)
}
}
#[cfg(test)]
@@ -652,4 +860,50 @@ pub(crate) mod tests {
assert!(v.is::<Foo>());
assert_eq!(42u64, v.downcast_ref::<Foo>().expect("invalid downcast").0);
}
#[test]
fn test_visit_integer() {
struct Extract(Option<u64>);
impl<'v> Visit<'v> for Extract {
fn visit_any(&mut self, value: Value) -> Result<(), Error> {
unimplemented!("unexpected value: {:?}", value)
}
fn visit_u64(&mut self, value: u64) -> Result<(), Error> {
self.0 = Some(value);
Ok(())
}
}
let mut extract = Extract(None);
Value::from(42u64).visit(&mut extract).unwrap();
assert_eq!(Some(42), extract.0);
}
#[test]
fn test_visit_borrowed_str() {
struct Extract<'v>(Option<&'v str>);
impl<'v> Visit<'v> for Extract<'v> {
fn visit_any(&mut self, value: Value) -> Result<(), Error> {
unimplemented!("unexpected value: {:?}", value)
}
fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> {
self.0 = Some(value);
Ok(())
}
}
let mut extract = Extract(None);
let short_lived = String::from("A short-lived string");
Value::from(&*short_lived).visit(&mut extract).unwrap();
assert_eq!(Some("A short-lived string"), extract.0);
}
}