make value internal visitor support borrowing

This commit is contained in:
Ashley Mannix 2020-01-31 15:02:59 +10:00
parent ddb118e466
commit 6743f4a0d2
4 changed files with 216 additions and 96 deletions

View File

@ -24,17 +24,9 @@ pub(super) enum Inner<'v> {
}
impl<'v> Inner<'v> {
pub(super) fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> {
match *self {
Inner::Primitive(value) => match value {
Primitive::Signed(value) => visitor.i64(value),
Primitive::Unsigned(value) => visitor.u64(value),
Primitive::Float(value) => visitor.f64(value),
Primitive::Bool(value) => visitor.bool(value),
Primitive::Char(value) => visitor.char(value),
Primitive::Str(value) => visitor.str(value),
Primitive::None => visitor.none(),
},
pub(super) fn visit(self, visitor: &mut dyn Visitor<'v>) -> Result<(), Error> {
match self {
Inner::Primitive(value) => value.visit(visitor),
Inner::Fill(value) => value.fill(&mut Slot::new(visitor)),
Inner::Debug(value) => visitor.debug(value),
Inner::Display(value) => visitor.display(value),
@ -46,7 +38,7 @@ impl<'v> Inner<'v> {
}
/// The internal serialization contract.
pub(super) trait Visitor {
pub(super) trait Visitor<'v> {
fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error>;
fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> {
self.debug(&format_args!("{}", v))
@ -57,7 +49,12 @@ pub(super) trait Visitor {
fn f64(&mut self, v: f64) -> Result<(), Error>;
fn bool(&mut self, v: bool) -> Result<(), Error>;
fn char(&mut self, v: char) -> Result<(), Error>;
fn str(&mut self, v: &str) -> Result<(), Error>;
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
self.str(v)
}
fn none(&mut self) -> Result<(), Error>;
#[cfg(feature = "kv_unstable_sval")]
@ -75,35 +72,45 @@ pub(super) enum Primitive<'v> {
None,
}
impl<'v> Primitive<'v> {
fn visit(self, visitor: &mut dyn Visitor<'v>) -> Result<(), Error> {
match self {
Primitive::Signed(value) => visitor.i64(value),
Primitive::Unsigned(value) => visitor.u64(value),
Primitive::Float(value) => visitor.f64(value),
Primitive::Bool(value) => visitor.bool(value),
Primitive::Char(value) => visitor.char(value),
Primitive::Str(value) => visitor.borrowed_str(value),
Primitive::None => visitor.none(),
}
}
}
mod coerce {
use super::*;
impl<'v> Inner<'v> {
pub(in crate::kv::value) fn as_str(&self) -> Option<&str> {
if let Inner::Primitive(Primitive::Str(value)) = self {
Some(value)
} else {
self.coerce().into_primitive().into_str()
}
pub(in crate::kv::value) fn get_str(&self) -> Option<&str> {
self.coerce().into_primitive().into_str()
}
pub(in crate::kv::value) fn as_u64(&self) -> Option<u64> {
pub(in crate::kv::value) fn get_u64(&self) -> Option<u64> {
self.coerce().into_primitive().into_u64()
}
pub(in crate::kv::value) fn as_i64(&self) -> Option<i64> {
pub(in crate::kv::value) fn get_i64(&self) -> Option<i64> {
self.coerce().into_primitive().into_i64()
}
pub(in crate::kv::value) fn as_f64(&self) -> Option<f64> {
pub(in crate::kv::value) fn get_f64(&self) -> Option<f64> {
self.coerce().into_primitive().into_f64()
}
pub(in crate::kv::value) fn as_char(&self) -> Option<char> {
pub(in crate::kv::value) fn get_char(&self) -> Option<char> {
self.coerce().into_primitive().into_char()
}
pub(in crate::kv::value) fn as_bool(&self) -> Option<bool> {
pub(in crate::kv::value) fn get_bool(&self) -> Option<bool> {
self.coerce().into_primitive().into_bool()
}
@ -116,7 +123,7 @@ mod coerce {
}
}
impl<'v> Visitor for Coerce<'v> {
impl<'v> Visitor<'v> for Coerce<'v> {
fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> {
Ok(())
}
@ -146,8 +153,13 @@ mod coerce {
Ok(())
}
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
self.0 = Coerced::Primitive(Primitive::Str(v));
Ok(())
}
#[cfg(not(feature = "std"))]
fn str(&mut self, v: &str) -> Result<(), Error> {
fn str(&mut self, _: &str) -> Result<(), Error> {
Ok(())
}
@ -185,6 +197,7 @@ mod coerce {
fn into_primitive(self) -> Primitive<'v> {
match self {
Coerced::Primitive(value) => value,
#[cfg(feature = "std")]
_ => Primitive::None,
}
}
@ -247,7 +260,7 @@ mod coerce {
use std::borrow::Cow;
impl<'v> Inner<'v> {
pub(in crate::kv::value) fn to_str(&self) -> Option<Cow<str>> {
pub(in crate::kv::value) fn get_string(&self) -> Option<Cow<str>> {
self.coerce().into_string()
}
}
@ -289,6 +302,36 @@ mod fmt_support {
}
}
impl<'s, 'f> Slot<'s, 'f> {
/// Fill the slot with a debuggable value.
///
/// The given value doesn't need to satisfy any particular lifetime constraints.
///
/// # Panics
///
/// Calling more than a single `fill` method on this slot will panic.
pub fn fill_debug<T>(&mut self, value: T) -> Result<(), Error>
where
T: fmt::Debug,
{
self.fill(|visitor| visitor.debug(&value))
}
/// Fill the slot with a displayable value.
///
/// The given value doesn't need to satisfy any particular lifetime constraints.
///
/// # Panics
///
/// Calling more than a single `fill` method on this slot will panic.
pub fn fill_display<T>(&mut self, value: T) -> Result<(), Error>
where
T: fmt::Display,
{
self.fill(|visitor| visitor.display(&value))
}
}
impl<'v> fmt::Debug for kv::Value<'v> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.visit(&mut FmtVisitor(f))?;
@ -307,7 +350,7 @@ mod fmt_support {
struct FmtVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>);
impl<'a, 'b: 'a> Visitor for FmtVisitor<'a, 'b> {
impl<'a, 'b: 'a, 'v> Visitor<'v> for FmtVisitor<'a, 'b> {
fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
v.fmt(self.0)?;
@ -368,6 +411,22 @@ pub(super) mod sval_support {
}
}
impl<'s, 'f> Slot<'s, 'f> {
/// Fill the slot with a structured value.
///
/// The given value doesn't need to satisfy any particular lifetime constraints.
///
/// # Panics
///
/// Calling more than a single `fill` method on this slot will panic.
pub fn fill_sval<T>(&mut self, value: T) -> Result<(), Error>
where
T: sval::Value,
{
self.fill(|visitor| visitor.sval(&value))
}
}
impl<'v> sval::Value for kv::Value<'v> {
fn stream(&self, s: &mut sval::value::Stream) -> sval::value::Result {
self.visit(&mut SvalVisitor(s)).map_err(Error::into_sval)?;
@ -395,7 +454,7 @@ pub(super) mod sval_support {
struct SvalVisitor<'a, 'b: 'a>(&'a mut sval::value::Stream<'b>);
impl<'a, 'b: 'a> Visitor for SvalVisitor<'a, 'b> {
impl<'a, 'b: 'a, 'v> Visitor<'v> for SvalVisitor<'a, 'b> {
fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
self.0
.fmt(format_args!("{:?}", v))
@ -496,21 +555,21 @@ pub(super) mod sval_support {
}
#[test]
fn coersion() {
fn sval_coersion() {
assert_eq!(
42u64,
kv::Value::from_sval(&42u64)
.as_u64()
.get_u64()
.expect("invalid value")
);
assert!(kv::Value::from_sval(&"a string").as_str().is_none());
assert!(kv::Value::from_sval(&"a string").get_str().is_none());
#[cfg(feature = "std")]
assert_eq!(
"a string",
&*kv::Value::from_sval(&"a string")
.to_str()
.get_string()
.expect("invalid value")
);
}

View File

@ -53,37 +53,47 @@ where
}
/// A value slot to fill using the [`Fill`](trait.Fill.html) trait.
pub struct Slot<'a> {
pub struct Slot<'s, 'f> {
filled: bool,
visitor: &'a mut dyn Visitor,
visitor: &'s mut dyn Visitor<'f>,
}
impl<'a> fmt::Debug for Slot<'a> {
impl<'s, 'f> fmt::Debug for Slot<'s, 'f> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Slot").finish()
}
}
impl<'a> Slot<'a> {
fn new(visitor: &'a mut dyn Visitor) -> Self {
impl<'s, 'f> Slot<'s, 'f> {
fn new(visitor: &'s mut dyn Visitor<'f>) -> Self {
Slot {
visitor,
filled: false,
}
}
fn fill<F>(&mut self, f: F) -> Result<(), Error>
where
F: FnOnce(&mut dyn Visitor<'f>) -> Result<(), Error>,
{
assert!(!self.filled, "the slot has already been filled");
self.filled = true;
f(self.visitor)
}
/// Fill the slot with a value.
///
/// The given value doesn't need to satisfy any particular lifetime constraints.
///
/// # Panics
///
/// Calling `fill` more than once will panic.
pub fn fill(&mut self, value: Value) -> Result<(), Error> {
assert!(!self.filled, "the slot has already been filled");
self.filled = true;
value.visit(self.visitor)
/// Calling more than a single `fill` method on this slot will panic.
pub fn fill_any<T>(&mut self, value: T) -> Result<(), Error>
where
T: Into<Value<'f>>,
{
self.fill(|visitor| value.into().inner.visit(visitor))
}
}
@ -111,71 +121,71 @@ impl<'v> Value<'v> {
}
/// Try coerce the value into a borrowed string.
pub fn as_str(&self) -> Option<&str> {
self.inner.as_str()
pub fn get_str(&self) -> Option<&str> {
self.inner.get_str()
}
/// Try coerce the value into a `u8`.
pub fn as_u8(&self) -> Option<u8> {
self.inner.as_u64().map(|v| v as u8)
pub fn get_u8(&self) -> Option<u8> {
self.inner.get_u64().map(|v| v as u8)
}
/// Try coerce the value into a `u16`.
pub fn as_u16(&self) -> Option<u16> {
self.inner.as_u64().map(|v| v as u16)
pub fn get_u16(&self) -> Option<u16> {
self.inner.get_u64().map(|v| v as u16)
}
/// Try coerce the value into a `u32`.
pub fn as_u32(&self) -> Option<u32> {
self.inner.as_u64().map(|v| v as u32)
pub fn get_u32(&self) -> Option<u32> {
self.inner.get_u64().map(|v| v as u32)
}
/// Try coerce the value into a `u64`.
pub fn as_u64(&self) -> Option<u64> {
self.inner.as_u64()
pub fn get_u64(&self) -> Option<u64> {
self.inner.get_u64()
}
/// Try coerce the value into a `i8`.
pub fn as_i8(&self) -> Option<i8> {
self.inner.as_i64().map(|v| v as i8)
pub fn get_i8(&self) -> Option<i8> {
self.inner.get_i64().map(|v| v as i8)
}
/// Try coerce the value into a `i16`.
pub fn as_i16(&self) -> Option<i16> {
self.inner.as_i64().map(|v| v as i16)
pub fn get_i16(&self) -> Option<i16> {
self.inner.get_i64().map(|v| v as i16)
}
/// Try coerce the value into a `i32`.
pub fn as_i32(&self) -> Option<i32> {
self.inner.as_i64().map(|v| v as i32)
pub fn get_i32(&self) -> Option<i32> {
self.inner.get_i64().map(|v| v as i32)
}
/// Try coerce the value into a `i64`.
pub fn as_i64(&self) -> Option<i64> {
self.inner.as_i64()
pub fn get_i64(&self) -> Option<i64> {
self.inner.get_i64()
}
/// Try coerce the value into a `f32`.
pub fn as_f32(&self) -> Option<f32> {
self.inner.as_f64().map(|v| v as f32)
pub fn get_f32(&self) -> Option<f32> {
self.inner.get_f64().map(|v| v as f32)
}
/// Try coerce the value into a `f64`.
pub fn as_f64(&self) -> Option<f64> {
self.inner.as_f64()
pub fn get_f64(&self) -> Option<f64> {
self.inner.get_f64()
}
/// Try coerce the vlaue into a `char`.
pub fn as_char(&self) -> Option<char> {
self.inner.as_char()
/// Try coerce the value into a `char`.
pub fn get_char(&self) -> Option<char> {
self.inner.get_char()
}
/// Try coerce the vlaue into a `bool`.
pub fn as_bool(&self) -> Option<bool> {
self.inner.as_bool()
/// Try coerce the value into a `bool`.
pub fn get_bool(&self) -> Option<bool> {
self.inner.get_bool()
}
fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> {
fn visit<'a>(&'a self, visitor: &mut dyn Visitor<'a>) -> Result<(), Error> {
self.inner.visit(visitor)
}
}
@ -188,8 +198,8 @@ mod std_support {
impl<'v> Value<'v> {
/// Try coerce the value into an owned or borrowed string.
pub fn to_str(&self) -> Option<Cow<str>> {
self.inner.to_str()
pub fn get_string(&self) -> Option<Cow<str>> {
self.inner.get_string()
}
}
@ -204,19 +214,19 @@ mod std_support {
"a string"
.to_owned()
.to_value()
.as_str()
.get_str()
.expect("invalid value")
);
assert_eq!(
"a string",
&*"a string".to_value().to_str().expect("invalid value")
&*"a string".to_value().get_string().expect("invalid value")
);
assert_eq!(
"a string",
&*"a string"
.to_owned()
.to_value()
.to_str()
.get_string()
.expect("invalid value")
);
}
@ -228,20 +238,31 @@ mod tests {
use super::*;
#[test]
fn fill_value() {
fn fill_value_borrowed() {
struct TestFill;
impl Fill for TestFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
let dbg: &dyn fmt::Debug = &1;
slot.fill(Value::from_debug(&dbg))
slot.fill_debug(&dbg)
}
}
assert_eq!("1", Value::from_fill(&TestFill).to_string());
}
#[test]
fn fill_value_owned() {
struct TestFill;
impl Fill for TestFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
slot.fill_any("a string")
}
}
}
#[test]
#[should_panic]
fn fill_multiple_times_panics() {
@ -249,8 +270,8 @@ mod tests {
impl Fill for BadFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
slot.fill(42.into())?;
slot.fill(6789.into())?;
slot.fill_any(42)?;
slot.fill_any(6789)?;
Ok(())
}
@ -263,27 +284,48 @@ mod tests {
fn primitive_coercion() {
assert_eq!(
"a string",
"a string".to_value().as_str().expect("invalid value")
"a string".to_value().get_str().expect("invalid value")
);
assert_eq!(
"a string",
Some("a string").to_value().as_str().expect("invalid value")
Some("a string")
.to_value()
.get_str()
.expect("invalid value")
);
assert_eq!(1u8, 1u64.to_value().as_u8().expect("invalid value"));
assert_eq!(1u16, 1u64.to_value().as_u16().expect("invalid value"));
assert_eq!(1u32, 1u64.to_value().as_u32().expect("invalid value"));
assert_eq!(1u64, 1u64.to_value().as_u64().expect("invalid value"));
assert_eq!(1u8, 1u64.to_value().get_u8().expect("invalid value"));
assert_eq!(1u16, 1u64.to_value().get_u16().expect("invalid value"));
assert_eq!(1u32, 1u64.to_value().get_u32().expect("invalid value"));
assert_eq!(1u64, 1u64.to_value().get_u64().expect("invalid value"));
assert_eq!(-1i8, -1i64.to_value().as_i8().expect("invalid value"));
assert_eq!(-1i16, -1i64.to_value().as_i16().expect("invalid value"));
assert_eq!(-1i32, -1i64.to_value().as_i32().expect("invalid value"));
assert_eq!(-1i64, -1i64.to_value().as_i64().expect("invalid value"));
assert_eq!(-1i8, -1i64.to_value().get_i8().expect("invalid value"));
assert_eq!(-1i16, -1i64.to_value().get_i16().expect("invalid value"));
assert_eq!(-1i32, -1i64.to_value().get_i32().expect("invalid value"));
assert_eq!(-1i64, -1i64.to_value().get_i64().expect("invalid value"));
assert!(1f32.to_value().as_f32().is_some(), "invalid value");
assert!(1f64.to_value().as_f64().is_some(), "invalid value");
assert!(1f32.to_value().get_f32().is_some(), "invalid value");
assert!(1f64.to_value().get_f64().is_some(), "invalid value");
assert_eq!('a', 'a'.to_value().as_char().expect("invalid value"));
assert_eq!(true, true.to_value().as_bool().expect("invalid value"));
assert_eq!('a', 'a'.to_value().get_char().expect("invalid value"));
assert_eq!(true, true.to_value().get_bool().expect("invalid value"));
}
#[test]
fn fill_coercion() {
struct TestFill;
impl Fill for TestFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
slot.fill_any("a string")
}
}
assert_eq!(
"a string",
Value::from_fill(&TestFill)
.get_str()
.expect("invalid value")
);
}
}

View File

@ -25,7 +25,7 @@ impl<'v> Value<'v> {
pub(in kv) fn to_token(&self) -> Token {
struct TestVisitor(Option<Token>);
impl internal::Visitor for TestVisitor {
impl<'v> internal::Visitor<'v> for TestVisitor {
fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
self.0 = Some(Token::Str(format!("{:?}", v)));
Ok(())

View File

@ -1648,4 +1648,23 @@ mod tests {
assert_eq!(2, visitor.seen_pairs);
}
#[test]
#[cfg(feature = "kv_unstable")]
fn test_record_key_values_get_coerce() {
use super::Record;
let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)];
let record = Record::builder().key_values(&kvs).build();
assert_eq!(
1,
record
.key_values()
.get("a".into())
.expect("missing key")
.get_i32()
.expect("invalid value")
);
}
}