mirror of
https://gitee.com/openharmony/third_party_rust_log
synced 2024-11-23 07:40:08 +00:00
Merge pull request #379 from KodrAus/feat/value-coercion
Support coercing structured Values into primitive types
This commit is contained in:
commit
d54317e47a
@ -51,8 +51,8 @@ kv_unstable_sval = ["kv_unstable", "sval/fmt"]
|
||||
[dependencies]
|
||||
cfg-if = "0.1.2"
|
||||
serde = { version = "1.0", optional = true, default-features = false }
|
||||
sval = { version = "0.4.2", optional = true, default-features = false }
|
||||
sval = { version = "0.5", optional = true, default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_test = "1.0"
|
||||
sval = { version = "0.4.2", features = ["test"] }
|
||||
sval = { version = "0.5", features = ["test"] }
|
||||
|
148
src/kv/value/fill.rs
Normal file
148
src/kv/value/fill.rs
Normal file
@ -0,0 +1,148 @@
|
||||
//! Lazy value initialization.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::internal::{Erased, Inner, Visitor};
|
||||
use super::{Error, Value};
|
||||
|
||||
impl<'v> Value<'v> {
|
||||
/// Get a value from a fillable slot.
|
||||
pub fn from_fill<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: Fill + 'static,
|
||||
{
|
||||
Value {
|
||||
inner: Inner::Fill(unsafe { Erased::new_unchecked::<T>(value) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that requires extra work to convert into a [`Value`](struct.Value.html).
|
||||
///
|
||||
/// This trait is a more advanced initialization API than [`ToValue`](trait.ToValue.html).
|
||||
/// It's intended for erased values coming from other logging frameworks that may need
|
||||
/// to perform extra work to determine the concrete type to use.
|
||||
pub trait Fill {
|
||||
/// Fill a value.
|
||||
fn fill(&self, slot: &mut Slot) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl<'a, T> Fill for &'a T
|
||||
where
|
||||
T: Fill + ?Sized,
|
||||
{
|
||||
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
|
||||
(**self).fill(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/// A value slot to fill using the [`Fill`](trait.Fill.html) trait.
|
||||
pub struct Slot<'s, 'f> {
|
||||
filled: bool,
|
||||
visitor: &'s mut dyn Visitor<'f>,
|
||||
}
|
||||
|
||||
impl<'s, 'f> fmt::Debug for Slot<'s, 'f> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("Slot").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, 'f> Slot<'s, 'f> {
|
||||
pub(super) fn new(visitor: &'s mut dyn Visitor<'f>) -> Self {
|
||||
Slot {
|
||||
visitor,
|
||||
filled: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) 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 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))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
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_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() {
|
||||
struct BadFill;
|
||||
|
||||
impl Fill for BadFill {
|
||||
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
|
||||
slot.fill_any(42)?;
|
||||
slot.fill_any(6789)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let _ = Value::from_fill(&BadFill).to_string();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fill_cast() {
|
||||
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)
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
}
|
||||
}
|
@ -1,173 +1,28 @@
|
||||
//! Converting standard types into `Value`s.
|
||||
//!
|
||||
//! This module provides `ToValue` implementations for commonly
|
||||
//! logged types from the standard library.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::{Primitive, ToValue, Value};
|
||||
|
||||
impl ToValue for usize {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
macro_rules! impl_into_owned {
|
||||
($($into_ty:ty => $convert:ident,)*) => {
|
||||
$(
|
||||
impl ToValue for $into_ty {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<usize> for Value<'v> {
|
||||
fn from(value: usize) -> Self {
|
||||
Value::from_primitive(Primitive::Unsigned(value as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for isize {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<isize> for Value<'v> {
|
||||
fn from(value: isize) -> Self {
|
||||
Value::from_primitive(Primitive::Signed(value as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for u8 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<u8> for Value<'v> {
|
||||
fn from(value: u8) -> Self {
|
||||
Value::from_primitive(Primitive::Unsigned(value as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for u16 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<u16> for Value<'v> {
|
||||
fn from(value: u16) -> Self {
|
||||
Value::from_primitive(Primitive::Unsigned(value as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for u32 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<u32> for Value<'v> {
|
||||
fn from(value: u32) -> Self {
|
||||
Value::from_primitive(Primitive::Unsigned(value as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for u64 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<u64> for Value<'v> {
|
||||
fn from(value: u64) -> Self {
|
||||
Value::from_primitive(Primitive::Unsigned(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for i8 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<i8> for Value<'v> {
|
||||
fn from(value: i8) -> Self {
|
||||
Value::from_primitive(Primitive::Signed(value as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for i16 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<i16> for Value<'v> {
|
||||
fn from(value: i16) -> Self {
|
||||
Value::from_primitive(Primitive::Signed(value as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for i32 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<i32> for Value<'v> {
|
||||
fn from(value: i32) -> Self {
|
||||
Value::from_primitive(Primitive::Signed(value as i64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for i64 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<i64> for Value<'v> {
|
||||
fn from(value: i64) -> Self {
|
||||
Value::from_primitive(Primitive::Signed(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for f32 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<f32> for Value<'v> {
|
||||
fn from(value: f32) -> Self {
|
||||
Value::from_primitive(Primitive::Float(value as f64))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for f64 {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<f64> for Value<'v> {
|
||||
fn from(value: f64) -> Self {
|
||||
Value::from_primitive(Primitive::Float(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for bool {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<bool> for Value<'v> {
|
||||
fn from(value: bool) -> Self {
|
||||
Value::from_primitive(Primitive::Bool(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToValue for char {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<char> for Value<'v> {
|
||||
fn from(value: char) -> Self {
|
||||
Value::from_primitive(Primitive::Char(value))
|
||||
}
|
||||
impl<'v> From<$into_ty> for Value<'v> {
|
||||
fn from(value: $into_ty) -> Self {
|
||||
Value::from_primitive(value as $convert)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl<'v> ToValue for &'v str {
|
||||
@ -178,7 +33,19 @@ impl<'v> ToValue for &'v str {
|
||||
|
||||
impl<'v> From<&'v str> for Value<'v> {
|
||||
fn from(value: &'v str) -> Self {
|
||||
Value::from_primitive(Primitive::Str(value))
|
||||
Value::from_primitive(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> ToValue for fmt::Arguments<'v> {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<fmt::Arguments<'v>> for Value<'v> {
|
||||
fn from(value: fmt::Arguments<'v>) -> Self {
|
||||
Value::from_primitive(value)
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,11 +67,25 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> ToValue for fmt::Arguments<'v> {
|
||||
fn to_value(&self) -> Value {
|
||||
Value::from_debug(self)
|
||||
}
|
||||
}
|
||||
impl_into_owned! [
|
||||
usize => u64,
|
||||
u8 => u64,
|
||||
u16 => u64,
|
||||
u32 => u64,
|
||||
u64 => u64,
|
||||
|
||||
isize => i64,
|
||||
i8 => i64,
|
||||
i16 => i64,
|
||||
i32 => i64,
|
||||
i64 => i64,
|
||||
|
||||
f32 => f64,
|
||||
f64 => f64,
|
||||
|
||||
char => char,
|
||||
bool => bool,
|
||||
];
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod std_support {
|
||||
|
@ -1,266 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use super::{Error, Fill, Slot};
|
||||
use kv;
|
||||
|
||||
// `Visitor` is an internal API for visiting the structure of a value.
|
||||
// It's not intended to be public (at this stage).
|
||||
|
||||
/// A container for a structured value for a specific kind of visitor.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) enum Inner<'v> {
|
||||
/// A simple primitive value that can be copied without allocating.
|
||||
Primitive(Primitive<'v>),
|
||||
/// A value that can be filled.
|
||||
Fill(&'v dyn Fill),
|
||||
/// A debuggable value.
|
||||
Debug(&'v dyn fmt::Debug),
|
||||
/// A displayable value.
|
||||
Display(&'v dyn fmt::Display),
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
/// A structured value from `sval`.
|
||||
Sval(&'v dyn sval_support::Value),
|
||||
}
|
||||
|
||||
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(),
|
||||
},
|
||||
Inner::Fill(value) => value.fill(&mut Slot::new(visitor)),
|
||||
Inner::Debug(value) => visitor.debug(value),
|
||||
Inner::Display(value) => visitor.display(value),
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
Inner::Sval(value) => visitor.sval(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal serialization contract.
|
||||
pub(super) trait Visitor {
|
||||
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))
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error>;
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error>;
|
||||
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 none(&mut self) -> Result<(), Error>;
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
fn sval(&mut self, v: &dyn sval_support::Value) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) enum Primitive<'v> {
|
||||
Signed(i64),
|
||||
Unsigned(u64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
Char(char),
|
||||
Str(&'v str),
|
||||
None,
|
||||
}
|
||||
|
||||
mod fmt_support {
|
||||
use super::*;
|
||||
|
||||
impl<'v> kv::Value<'v> {
|
||||
/// Get a value from a debuggable type.
|
||||
pub fn from_debug<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
kv::Value {
|
||||
inner: Inner::Debug(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a value from a displayable type.
|
||||
pub fn from_display<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: fmt::Display,
|
||||
{
|
||||
kv::Value {
|
||||
inner: Inner::Display(value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> fmt::Debug for kv::Value<'v> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.visit(&mut FmtVisitor(f))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> fmt::Display for kv::Value<'v> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.visit(&mut FmtVisitor(f))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct FmtVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>);
|
||||
|
||||
impl<'a, 'b: 'a> Visitor for FmtVisitor<'a, 'b> {
|
||||
fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
|
||||
v.fmt(self.0)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn char(&mut self, v: char) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn str(&mut self, v: &str) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<(), Error> {
|
||||
self.debug(&format_args!("None"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
fn sval(&mut self, v: &dyn sval_support::Value) -> Result<(), Error> {
|
||||
sval_support::fmt(self.0, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
pub(super) mod sval_support {
|
||||
use super::*;
|
||||
|
||||
extern crate sval;
|
||||
|
||||
impl<'v> kv::Value<'v> {
|
||||
/// Get a value from a structured type.
|
||||
pub fn from_sval<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: sval::Value,
|
||||
{
|
||||
kv::Value {
|
||||
inner: Inner::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)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(in kv::value) use self::sval::Value;
|
||||
|
||||
pub(super) fn fmt(f: &mut fmt::Formatter, v: &dyn sval::Value) -> Result<(), Error> {
|
||||
sval::fmt::debug(f, v)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn from_sval(_: sval::value::Error) -> Self {
|
||||
Error::msg("`sval` serialization failed")
|
||||
}
|
||||
|
||||
fn into_sval(self) -> sval::value::Error {
|
||||
sval::value::Error::msg("`sval` serialization failed")
|
||||
}
|
||||
}
|
||||
|
||||
struct SvalVisitor<'a, 'b: 'a>(&'a mut sval::value::Stream<'b>);
|
||||
|
||||
impl<'a, 'b: 'a> Visitor for SvalVisitor<'a, 'b> {
|
||||
fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
|
||||
self.0
|
||||
.fmt(format_args!("{:?}", v))
|
||||
.map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.0.u64(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.0.i64(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.0.f64(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.0.bool(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn char(&mut self, v: char) -> Result<(), Error> {
|
||||
self.0.char(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn str(&mut self, v: &str) -> Result<(), Error> {
|
||||
self.0.str(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<(), Error> {
|
||||
self.0.none().map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn sval(&mut self, v: &dyn sval::Value) -> Result<(), Error> {
|
||||
self.0.any(v).map_err(Error::from_sval)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kv::value::test::Token;
|
||||
|
||||
#[test]
|
||||
fn test_from_sval() {
|
||||
assert_eq!(kv::Value::from_sval(&42u64).to_token(), Token::Sval);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sval_structured() {
|
||||
let value = kv::Value::from(42u64);
|
||||
let expected = vec![sval::test::Token::Unsigned(42)];
|
||||
|
||||
assert_eq!(sval::test::tokens(value), expected);
|
||||
}
|
||||
}
|
||||
}
|
475
src/kv/value/internal/cast.rs
Normal file
475
src/kv/value/internal/cast.rs
Normal file
@ -0,0 +1,475 @@
|
||||
//! Coerce a `Value` into some concrete types.
|
||||
//!
|
||||
//! These operations are cheap when the captured value is a simple primitive,
|
||||
//! but may end up executing arbitrary caller code if the value is complex.
|
||||
//! They will also attempt to downcast erased types into a primitive where possible.
|
||||
|
||||
use std::any::TypeId;
|
||||
use std::fmt;
|
||||
|
||||
use super::{Erased, Inner, Primitive, Visitor};
|
||||
use crate::kv::value::{Error, Value};
|
||||
|
||||
impl<'v> Value<'v> {
|
||||
/// Try get a `usize` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_usize(&self) -> Option<usize> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_u64()
|
||||
.map(|v| v as usize)
|
||||
}
|
||||
|
||||
/// Try get a `u8` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_u8(&self) -> Option<u8> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_u64()
|
||||
.map(|v| v as u8)
|
||||
}
|
||||
|
||||
/// Try get a `u16` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_u16(&self) -> Option<u16> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_u64()
|
||||
.map(|v| v as u16)
|
||||
}
|
||||
|
||||
/// Try get a `u32` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_u32(&self) -> Option<u32> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_u64()
|
||||
.map(|v| v as u32)
|
||||
}
|
||||
|
||||
/// Try get a `u64` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_u64(&self) -> Option<u64> {
|
||||
self.inner.cast().into_primitive().into_u64()
|
||||
}
|
||||
|
||||
/// Try get a `isize` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_isize(&self) -> Option<isize> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_i64()
|
||||
.map(|v| v as isize)
|
||||
}
|
||||
|
||||
/// Try get a `i8` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_i8(&self) -> Option<i8> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_i64()
|
||||
.map(|v| v as i8)
|
||||
}
|
||||
|
||||
/// Try get a `i16` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_i16(&self) -> Option<i16> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_i64()
|
||||
.map(|v| v as i16)
|
||||
}
|
||||
|
||||
/// Try get a `i32` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_i32(&self) -> Option<i32> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_i64()
|
||||
.map(|v| v as i32)
|
||||
}
|
||||
|
||||
/// Try get a `i64` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_i64(&self) -> Option<i64> {
|
||||
self.inner.cast().into_primitive().into_i64()
|
||||
}
|
||||
|
||||
/// Try get a `f32` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_f32(&self) -> Option<f32> {
|
||||
self.inner
|
||||
.cast()
|
||||
.into_primitive()
|
||||
.into_f64()
|
||||
.map(|v| v as f32)
|
||||
}
|
||||
|
||||
/// Try get a `f64` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_f64(&self) -> Option<f64> {
|
||||
self.inner.cast().into_primitive().into_f64()
|
||||
}
|
||||
|
||||
/// Try get a `bool` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_bool(&self) -> Option<bool> {
|
||||
self.inner.cast().into_primitive().into_bool()
|
||||
}
|
||||
|
||||
/// Try get a `char` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones.
|
||||
pub fn to_char(&self) -> Option<char> {
|
||||
self.inner.cast().into_primitive().into_char()
|
||||
}
|
||||
|
||||
/// Try get a `str` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types. It won't allocate an owned
|
||||
/// `String` if the value is a complex type.
|
||||
pub fn to_borrowed_str(&self) -> Option<&str> {
|
||||
self.inner.cast().into_primitive().into_borrowed_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Inner<'v> {
|
||||
/// Cast the inner value to another type.
|
||||
fn cast(self) -> Cast<'v> {
|
||||
struct CastVisitor<'v>(Cast<'v>);
|
||||
|
||||
impl<'v> Visitor<'v> for CastVisitor<'v> {
|
||||
fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::Unsigned(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::Signed(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::Float(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::Bool(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn char(&mut self, v: char) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::Char(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::Str(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
fn str(&mut self, _: &str) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn str(&mut self, v: &str) -> Result<(), Error> {
|
||||
self.0 = Cast::String(v.into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<(), Error> {
|
||||
self.0 = Cast::Primitive(Primitive::None);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
fn sval(&mut self, v: &dyn super::sval::Value) -> Result<(), Error> {
|
||||
self.0 = super::sval::cast(v);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Try downcast an erased value first
|
||||
// It also lets us avoid the Visitor infrastructure for simple primitives
|
||||
let primitive = match self {
|
||||
Inner::Primitive(value) => Some(value),
|
||||
Inner::Fill(value) => value.downcast_primitive(),
|
||||
Inner::Debug(value) => value.downcast_primitive(),
|
||||
Inner::Display(value) => value.downcast_primitive(),
|
||||
|
||||
#[cfg(feature = "sval")]
|
||||
Inner::Sval(value) => value.downcast_primitive(),
|
||||
};
|
||||
|
||||
primitive.map(Cast::Primitive).unwrap_or_else(|| {
|
||||
// If the erased value isn't a primitive then we visit it
|
||||
let mut cast = CastVisitor(Cast::Primitive(Primitive::None));
|
||||
let _ = self.visit(&mut cast);
|
||||
cast.0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) enum Cast<'v> {
|
||||
Primitive(Primitive<'v>),
|
||||
#[cfg(feature = "std")]
|
||||
String(String),
|
||||
}
|
||||
|
||||
impl<'v> Cast<'v> {
|
||||
fn into_primitive(self) -> Primitive<'v> {
|
||||
match self {
|
||||
Cast::Primitive(value) => value,
|
||||
#[cfg(feature = "std")]
|
||||
_ => Primitive::None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Primitive<'v> {
|
||||
fn into_borrowed_str(self) -> Option<&'v str> {
|
||||
if let Primitive::Str(value) = self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn into_u64(self) -> Option<u64> {
|
||||
match self {
|
||||
Primitive::Unsigned(value) => Some(value),
|
||||
Primitive::Signed(value) => Some(value as u64),
|
||||
Primitive::Float(value) => Some(value as u64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_i64(self) -> Option<i64> {
|
||||
match self {
|
||||
Primitive::Signed(value) => Some(value),
|
||||
Primitive::Unsigned(value) => Some(value as i64),
|
||||
Primitive::Float(value) => Some(value as i64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_f64(self) -> Option<f64> {
|
||||
match self {
|
||||
Primitive::Float(value) => Some(value),
|
||||
Primitive::Unsigned(value) => Some(value as f64),
|
||||
Primitive::Signed(value) => Some(value as f64),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn into_char(self) -> Option<char> {
|
||||
if let Primitive::Char(value) = self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn into_bool(self) -> Option<bool> {
|
||||
if let Primitive::Bool(value) = self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v, T: ?Sized + 'static> Erased<'v, T> {
|
||||
// NOTE: This function is a perfect candidate for memoization
|
||||
// The outcome could be stored in a `Cell<Primitive>`
|
||||
fn downcast_primitive(self) -> Option<Primitive<'v>> {
|
||||
macro_rules! type_ids {
|
||||
($($value:ident : $ty:ty => $cast:expr,)*) => {{
|
||||
struct TypeIds;
|
||||
|
||||
impl TypeIds {
|
||||
fn downcast_primitive<'v, T: ?Sized>(&self, value: Erased<'v, T>) -> Option<Primitive<'v>> {
|
||||
$(
|
||||
if TypeId::of::<$ty>() == value.type_id {
|
||||
let $value = unsafe { value.downcast_unchecked::<$ty>() };
|
||||
return Some(Primitive::from($cast));
|
||||
}
|
||||
)*
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
TypeIds
|
||||
}};
|
||||
}
|
||||
|
||||
let type_ids = type_ids![
|
||||
value: usize => *value as u64,
|
||||
value: u8 => *value as u64,
|
||||
value: u16 => *value as u64,
|
||||
value: u32 => *value as u64,
|
||||
value: u64 => *value,
|
||||
|
||||
value: isize => *value as i64,
|
||||
value: i8 => *value as i64,
|
||||
value: i16 => *value as i64,
|
||||
value: i32 => *value as i64,
|
||||
value: i64 => *value,
|
||||
|
||||
value: f32 => *value as f64,
|
||||
value: f64 => *value,
|
||||
|
||||
value: char => *value,
|
||||
value: bool => *value,
|
||||
|
||||
value: &str => *value,
|
||||
];
|
||||
|
||||
type_ids.downcast_primitive(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod std_support {
|
||||
use super::*;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
impl<'v> Value<'v> {
|
||||
/// Try get a `usize` from this value.
|
||||
///
|
||||
/// This method is cheap for primitive types, but may call arbitrary
|
||||
/// serialization implementations for complex ones. If the serialization
|
||||
/// implementation produces a short lived string it will be allocated.
|
||||
pub fn to_str(&self) -> Option<Cow<str>> {
|
||||
self.inner.cast().into_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> Cast<'v> {
|
||||
pub(super) fn into_str(self) -> Option<Cow<'v, str>> {
|
||||
match self {
|
||||
Cast::Primitive(Primitive::Str(value)) => Some(value.into()),
|
||||
Cast::String(value) => Some(value.into()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::kv::ToValue;
|
||||
|
||||
#[test]
|
||||
fn primitive_cast() {
|
||||
assert_eq!(
|
||||
"a string",
|
||||
"a string"
|
||||
.to_owned()
|
||||
.to_value()
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
assert_eq!(
|
||||
"a string",
|
||||
&*"a string".to_value().to_str().expect("invalid value")
|
||||
);
|
||||
assert_eq!(
|
||||
"a string",
|
||||
&*"a string"
|
||||
.to_owned()
|
||||
.to_value()
|
||||
.to_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::kv::ToValue;
|
||||
|
||||
#[test]
|
||||
fn primitive_cast() {
|
||||
assert_eq!(
|
||||
"a string",
|
||||
"a string"
|
||||
.to_value()
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
assert_eq!(
|
||||
"a string",
|
||||
Some("a string")
|
||||
.to_value()
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
|
||||
assert_eq!(1u8, 1u64.to_value().to_u8().expect("invalid value"));
|
||||
assert_eq!(1u16, 1u64.to_value().to_u16().expect("invalid value"));
|
||||
assert_eq!(1u32, 1u64.to_value().to_u32().expect("invalid value"));
|
||||
assert_eq!(1u64, 1u64.to_value().to_u64().expect("invalid value"));
|
||||
assert_eq!(1usize, 1u64.to_value().to_usize().expect("invalid value"));
|
||||
|
||||
assert_eq!(-1i8, -1i64.to_value().to_i8().expect("invalid value"));
|
||||
assert_eq!(-1i16, -1i64.to_value().to_i16().expect("invalid value"));
|
||||
assert_eq!(-1i32, -1i64.to_value().to_i32().expect("invalid value"));
|
||||
assert_eq!(-1i64, -1i64.to_value().to_i64().expect("invalid value"));
|
||||
assert_eq!(-1isize, -1i64.to_value().to_isize().expect("invalid value"));
|
||||
|
||||
assert!(1f32.to_value().to_f32().is_some(), "invalid value");
|
||||
assert!(1f64.to_value().to_f64().is_some(), "invalid value");
|
||||
|
||||
assert_eq!(1u32, 1i64.to_value().to_u32().expect("invalid value"));
|
||||
assert_eq!(1i32, 1u64.to_value().to_i32().expect("invalid value"));
|
||||
assert!(1f32.to_value().to_i32().is_some(), "invalid value");
|
||||
|
||||
assert_eq!('a', 'a'.to_value().to_char().expect("invalid value"));
|
||||
assert_eq!(true, true.to_value().to_bool().expect("invalid value"));
|
||||
}
|
||||
}
|
145
src/kv/value/internal/fmt.rs
Normal file
145
src/kv/value/internal/fmt.rs
Normal file
@ -0,0 +1,145 @@
|
||||
//! Integration between `Value` and `std::fmt`.
|
||||
//!
|
||||
//! This module allows any `Value` to implement the `fmt::Debug` and `fmt::Display` traits,
|
||||
//! and for any `fmt::Debug` or `fmt::Display` to be captured as a `Value`.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::{Erased, Inner, Visitor};
|
||||
use crate::kv;
|
||||
use crate::kv::value::{Error, Slot};
|
||||
|
||||
impl<'v> kv::Value<'v> {
|
||||
/// Get a value from a debuggable type.
|
||||
pub fn from_debug<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: fmt::Debug + 'static,
|
||||
{
|
||||
kv::Value {
|
||||
inner: Inner::Debug(unsafe { Erased::new_unchecked::<T>(value) }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a value from a displayable type.
|
||||
pub fn from_display<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: fmt::Display + 'static,
|
||||
{
|
||||
kv::Value {
|
||||
inner: Inner::Display(unsafe { Erased::new_unchecked::<T>(value) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
pub(in kv::value) use self::fmt::{Arguments, Debug, Display};
|
||||
|
||||
impl<'v> fmt::Debug for kv::Value<'v> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.visit(&mut FmtVisitor(f))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> fmt::Display for kv::Value<'v> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.visit(&mut FmtVisitor(f))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct FmtVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'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)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn char(&mut self, v: char) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn str(&mut self, v: &str) -> Result<(), Error> {
|
||||
self.debug(&format_args!("{:?}", v))
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<(), Error> {
|
||||
self.debug(&format_args!("None"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
fn sval(&mut self, v: &dyn super::sval::Value) -> Result<(), Error> {
|
||||
super::sval::fmt(self.0, v)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fmt_cast() {
|
||||
assert_eq!(
|
||||
42u32,
|
||||
kv::Value::from_debug(&42u64)
|
||||
.to_u32()
|
||||
.expect("invalid value")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"a string",
|
||||
kv::Value::from_display(&"a string")
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
}
|
||||
}
|
181
src/kv/value/internal/mod.rs
Normal file
181
src/kv/value/internal/mod.rs
Normal file
@ -0,0 +1,181 @@
|
||||
//! The internal `Value` serialization API.
|
||||
//!
|
||||
//! This implementation isn't intended to be public. It may need to change
|
||||
//! for optimizations or to support new external serialization frameworks.
|
||||
|
||||
use std::any::TypeId;
|
||||
|
||||
use super::{Error, Fill, Slot};
|
||||
|
||||
pub(super) mod cast;
|
||||
pub(super) mod fmt;
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
pub(super) mod sval;
|
||||
|
||||
/// A container for a structured value for a specific kind of visitor.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) enum Inner<'v> {
|
||||
/// A simple primitive value that can be copied without allocating.
|
||||
Primitive(Primitive<'v>),
|
||||
/// A value that can be filled.
|
||||
Fill(Erased<'v, dyn Fill + 'static>),
|
||||
/// A debuggable value.
|
||||
Debug(Erased<'v, dyn fmt::Debug + 'static>),
|
||||
/// A displayable value.
|
||||
Display(Erased<'v, dyn fmt::Display + 'static>),
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
/// A structured value from `sval`.
|
||||
Sval(Erased<'v, dyn sval::Value + 'static>),
|
||||
}
|
||||
|
||||
impl<'v> Inner<'v> {
|
||||
pub(super) fn visit(self, visitor: &mut dyn Visitor<'v>) -> Result<(), Error> {
|
||||
match self {
|
||||
Inner::Primitive(value) => value.visit(visitor),
|
||||
Inner::Fill(value) => value.get().fill(&mut Slot::new(visitor)),
|
||||
Inner::Debug(value) => visitor.debug(value.get()),
|
||||
Inner::Display(value) => visitor.display(value.get()),
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
Inner::Sval(value) => visitor.sval(value.get()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal serialization contract.
|
||||
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))
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error>;
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error>;
|
||||
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")]
|
||||
fn sval(&mut self, v: &dyn sval::Value) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// A captured primitive value.
|
||||
///
|
||||
/// These values are common and cheap to copy around.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(super) enum Primitive<'v> {
|
||||
Signed(i64),
|
||||
Unsigned(u64),
|
||||
Float(f64),
|
||||
Bool(bool),
|
||||
Char(char),
|
||||
Str(&'v str),
|
||||
Fmt(fmt::Arguments<'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::Fmt(value) => visitor.debug(&value),
|
||||
Primitive::None => visitor.none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<u64> for Primitive<'v> {
|
||||
fn from(v: u64) -> Self {
|
||||
Primitive::Unsigned(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<i64> for Primitive<'v> {
|
||||
fn from(v: i64) -> Self {
|
||||
Primitive::Signed(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<f64> for Primitive<'v> {
|
||||
fn from(v: f64) -> Self {
|
||||
Primitive::Float(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<bool> for Primitive<'v> {
|
||||
fn from(v: bool) -> Self {
|
||||
Primitive::Bool(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<char> for Primitive<'v> {
|
||||
fn from(v: char) -> Self {
|
||||
Primitive::Char(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<&'v str> for Primitive<'v> {
|
||||
fn from(v: &'v str) -> Self {
|
||||
Primitive::Str(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v> From<fmt::Arguments<'v>> for Primitive<'v> {
|
||||
fn from(v: fmt::Arguments<'v>) -> Self {
|
||||
Primitive::Fmt(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// A downcastable dynamic type.
|
||||
pub(super) struct Erased<'v, T: ?Sized> {
|
||||
type_id: TypeId,
|
||||
inner: &'v T,
|
||||
}
|
||||
|
||||
impl<'v, T: ?Sized> Clone for Erased<'v, T> {
|
||||
fn clone(&self) -> Self {
|
||||
Erased {
|
||||
type_id: self.type_id,
|
||||
inner: self.inner,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'v, T: ?Sized> Copy for Erased<'v, T> {}
|
||||
|
||||
impl<'v, T: ?Sized> Erased<'v, T> {
|
||||
// SAFETY: `U: Unsize<T>` and the underlying value `T` must not change
|
||||
// We could add a safe variant of this method with the `Unsize` trait
|
||||
pub(super) unsafe fn new_unchecked<U>(inner: &'v T) -> Self
|
||||
where
|
||||
U: 'static,
|
||||
T: 'static,
|
||||
{
|
||||
Erased {
|
||||
type_id: TypeId::of::<U>(),
|
||||
inner,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn get(self) -> &'v T {
|
||||
self.inner
|
||||
}
|
||||
|
||||
// SAFETY: The underlying type of `T` is `U`
|
||||
pub(super) unsafe fn downcast_unchecked<U>(self) -> &'v U {
|
||||
&*(self.inner as *const T as *const U)
|
||||
}
|
||||
}
|
194
src/kv/value/internal/sval.rs
Normal file
194
src/kv/value/internal/sval.rs
Normal file
@ -0,0 +1,194 @@
|
||||
//! Integration between `Value` and `sval`.
|
||||
//!
|
||||
//! This module allows any `Value` to implement the `sval::Value` trait,
|
||||
//! and for any `sval::Value` to be captured as a `Value`.
|
||||
|
||||
extern crate sval;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use super::cast::Cast;
|
||||
use super::{Erased, Inner, Primitive, Visitor};
|
||||
use crate::kv;
|
||||
use crate::kv::value::{Error, Slot};
|
||||
|
||||
impl<'v> kv::Value<'v> {
|
||||
/// Get a value from a structured type.
|
||||
pub fn from_sval<T>(value: &'v T) -> Self
|
||||
where
|
||||
T: sval::Value + 'static,
|
||||
{
|
||||
kv::Value {
|
||||
inner: Inner::Sval(unsafe { Erased::new_unchecked::<T>(value) }),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(in kv::value) use self::sval::Value;
|
||||
|
||||
pub(super) fn fmt(f: &mut fmt::Formatter, v: &dyn sval::Value) -> Result<(), Error> {
|
||||
sval::fmt::debug(f, v)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn cast<'v>(v: &dyn sval::Value) -> Cast<'v> {
|
||||
struct CastStream<'v>(Cast<'v>);
|
||||
|
||||
impl<'v> sval::Stream for CastStream<'v> {
|
||||
fn u64(&mut self, v: u64) -> sval::stream::Result {
|
||||
self.0 = Cast::Primitive(Primitive::Unsigned(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn i64(&mut self, v: i64) -> sval::stream::Result {
|
||||
self.0 = Cast::Primitive(Primitive::Signed(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn f64(&mut self, v: f64) -> sval::stream::Result {
|
||||
self.0 = Cast::Primitive(Primitive::Float(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn char(&mut self, v: char) -> sval::stream::Result {
|
||||
self.0 = Cast::Primitive(Primitive::Char(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) -> sval::stream::Result {
|
||||
self.0 = Cast::Primitive(Primitive::Bool(v));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
fn str(&mut self, s: &str) -> sval::stream::Result {
|
||||
self.0 = Cast::String(s.into());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut cast = CastStream(Cast::Primitive(Primitive::None));
|
||||
let _ = sval::stream(&mut cast, v);
|
||||
|
||||
cast.0
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn from_sval(_: sval::value::Error) -> Self {
|
||||
Error::msg("`sval` serialization failed")
|
||||
}
|
||||
|
||||
fn into_sval(self) -> sval::value::Error {
|
||||
sval::value::Error::msg("`sval` serialization failed")
|
||||
}
|
||||
}
|
||||
|
||||
struct SvalVisitor<'a, 'b: 'a>(&'a mut sval::value::Stream<'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))
|
||||
.map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn u64(&mut self, v: u64) -> Result<(), Error> {
|
||||
self.0.u64(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn i64(&mut self, v: i64) -> Result<(), Error> {
|
||||
self.0.i64(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn f64(&mut self, v: f64) -> Result<(), Error> {
|
||||
self.0.f64(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn bool(&mut self, v: bool) -> Result<(), Error> {
|
||||
self.0.bool(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn char(&mut self, v: char) -> Result<(), Error> {
|
||||
self.0.char(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn str(&mut self, v: &str) -> Result<(), Error> {
|
||||
self.0.str(v).map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn none(&mut self) -> Result<(), Error> {
|
||||
self.0.none().map_err(Error::from_sval)
|
||||
}
|
||||
|
||||
fn sval(&mut self, v: &dyn sval::Value) -> Result<(), Error> {
|
||||
self.0.any(v).map_err(Error::from_sval)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use kv::value::test::Token;
|
||||
|
||||
#[test]
|
||||
fn test_from_sval() {
|
||||
assert_eq!(kv::Value::from_sval(&42u64).to_token(), Token::Sval);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sval_structured() {
|
||||
let value = kv::Value::from(42u64);
|
||||
let expected = vec![sval::test::Token::Unsigned(42)];
|
||||
|
||||
assert_eq!(sval::test::tokens(value), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sval_cast() {
|
||||
assert_eq!(
|
||||
42u32,
|
||||
kv::Value::from_sval(&42u64)
|
||||
.to_u32()
|
||||
.expect("invalid value")
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"a string",
|
||||
kv::Value::from_sval(&"a string")
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
assert_eq!(
|
||||
"a string",
|
||||
kv::Value::from_sval(&"a string")
|
||||
.to_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
//! Structured values.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
mod fill;
|
||||
mod impls;
|
||||
mod internal;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(in kv) mod test;
|
||||
|
||||
pub use self::fill::{Fill, Slot};
|
||||
pub use kv::Error;
|
||||
|
||||
use self::internal::{Inner, Primitive, Visitor};
|
||||
@ -33,121 +33,24 @@ impl<'v> ToValue for Value<'v> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that requires extra work to convert into a [`Value`](struct.Value.html).
|
||||
///
|
||||
/// This trait is a more advanced initialization API than [`ToValue`](trait.ToValue.html).
|
||||
/// It's intended for erased values coming from other logging frameworks that may need
|
||||
/// to perform extra work to determine the concrete type to use.
|
||||
pub trait Fill {
|
||||
/// Fill a value.
|
||||
fn fill(&self, slot: &mut Slot) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl<'a, T> Fill for &'a T
|
||||
where
|
||||
T: Fill + ?Sized,
|
||||
{
|
||||
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
|
||||
(**self).fill(slot)
|
||||
}
|
||||
}
|
||||
|
||||
/// A value slot to fill using the [`Fill`](trait.Fill.html) trait.
|
||||
pub struct Slot<'a> {
|
||||
filled: bool,
|
||||
visitor: &'a mut dyn Visitor,
|
||||
}
|
||||
|
||||
impl<'a> fmt::Debug for Slot<'a> {
|
||||
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 {
|
||||
Slot {
|
||||
visitor,
|
||||
filled: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// A value in a structured key-value pair.
|
||||
pub struct Value<'v> {
|
||||
inner: Inner<'v>,
|
||||
}
|
||||
|
||||
impl<'v> Value<'v> {
|
||||
/// Get a value from an internal `Visit`.
|
||||
fn from_primitive(value: Primitive<'v>) -> Self {
|
||||
Value {
|
||||
inner: Inner::Primitive(value),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a value from a fillable slot.
|
||||
pub fn from_fill<T>(value: &'v T) -> Self
|
||||
/// Get a value from an internal primitive.
|
||||
fn from_primitive<T>(value: T) -> Self
|
||||
where
|
||||
T: Fill,
|
||||
T: Into<Primitive<'v>>,
|
||||
{
|
||||
Value {
|
||||
inner: Inner::Fill(value),
|
||||
inner: Inner::Primitive(value.into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit(&self, visitor: &mut dyn Visitor) -> Result<(), Error> {
|
||||
/// Visit the value using an internal visitor.
|
||||
fn visit<'a>(&'a self, visitor: &mut dyn Visitor<'a>) -> Result<(), Error> {
|
||||
self.inner.visit(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn fill_value() {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!("1", Value::from_fill(&TestFill).to_string());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn fill_multiple_times_panics() {
|
||||
struct BadFill;
|
||||
|
||||
impl Fill for BadFill {
|
||||
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
|
||||
slot.fill(42.into())?;
|
||||
slot.fill(6789.into())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let _ = Value::from_fill(&BadFill).to_string();
|
||||
}
|
||||
}
|
||||
|
@ -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(())
|
||||
@ -67,7 +67,7 @@ impl<'v> Value<'v> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "kv_unstable_sval")]
|
||||
fn sval(&mut self, _: &dyn internal::sval_support::Value) -> Result<(), Error> {
|
||||
fn sval(&mut self, _: &dyn internal::sval::Value) -> Result<(), Error> {
|
||||
self.0 = Some(Token::Sval);
|
||||
Ok(())
|
||||
}
|
||||
|
20
src/lib.rs
20
src/lib.rs
@ -1530,7 +1530,6 @@ mod tests {
|
||||
#[cfg(feature = "std")]
|
||||
fn test_error_trait() {
|
||||
use super::SetLoggerError;
|
||||
use std::error::Error;
|
||||
let e = SetLoggerError(());
|
||||
assert_eq!(
|
||||
&e.to_string(),
|
||||
@ -1649,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, &str)] = &[("a", "1"), ("b", "2")];
|
||||
let record = Record::builder().key_values(&kvs).build();
|
||||
|
||||
assert_eq!(
|
||||
"2",
|
||||
record
|
||||
.key_values()
|
||||
.get("b".into())
|
||||
.expect("missing key")
|
||||
.to_borrowed_str()
|
||||
.expect("invalid value")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user