use alloc::{boxed::Box, string::String};
use core::{
borrow::Borrow,
fmt::{self, Write},
hash::{Hash, Hasher},
num,
ops::Range,
};
use self::private::ValidLen;
use crate::callsite;
#[derive(Debug)]
pub struct Field {
i: usize,
fields: FieldSet,
}
#[derive(Debug, Eq, PartialEq)]
pub struct Empty;
pub struct FieldSet {
names: &'static [&'static str],
callsite: callsite::Identifier,
}
pub struct ValueSet<'a> {
values: Values<'a>,
fields: &'a FieldSet,
}
enum Values<'a> {
Explicit(&'a [(&'a Field, Option<&'a (dyn Value + 'a)>)]),
All(&'a [Option<&'a (dyn Value + 'a)>]),
}
#[derive(Debug)]
pub struct Iter {
idxs: Range<usize>,
fields: FieldSet,
}
pub trait Visit {
#[cfg(all(tracing_unstable, feature = "valuable"))]
#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
fn record_value(&mut self, field: &Field, value: valuable::Value<'_>) {
self.record_debug(field, &value)
}
fn record_f64(&mut self, field: &Field, value: f64) {
self.record_debug(field, &value)
}
fn record_i64(&mut self, field: &Field, value: i64) {
self.record_debug(field, &value)
}
fn record_u64(&mut self, field: &Field, value: u64) {
self.record_debug(field, &value)
}
fn record_i128(&mut self, field: &Field, value: i128) {
self.record_debug(field, &value)
}
fn record_u128(&mut self, field: &Field, value: u128) {
self.record_debug(field, &value)
}
fn record_bool(&mut self, field: &Field, value: bool) {
self.record_debug(field, &value)
}
fn record_str(&mut self, field: &Field, value: &str) {
self.record_debug(field, &value)
}
fn record_bytes(&mut self, field: &Field, value: &[u8]) {
self.record_debug(field, &HexBytes(value))
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) {
self.record_debug(field, &DisplayValue(value))
}
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug);
}
pub trait Value: crate::sealed::Sealed {
fn record(&self, key: &Field, visitor: &mut dyn Visit);
}
#[derive(Clone)]
pub struct DisplayValue<T: fmt::Display>(T);
#[derive(Clone)]
pub struct DebugValue<T: fmt::Debug>(T);
pub fn display<T>(t: T) -> DisplayValue<T>
where
T: fmt::Display,
{
DisplayValue(t)
}
pub fn debug<T>(t: T) -> DebugValue<T>
where
T: fmt::Debug,
{
DebugValue(t)
}
#[cfg(all(tracing_unstable, feature = "valuable"))]
#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
pub fn valuable<T>(t: &T) -> valuable::Value<'_>
where
T: valuable::Valuable,
{
t.as_value()
}
struct HexBytes<'a>(&'a [u8]);
impl fmt::Debug for HexBytes<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_char('[')?;
let mut bytes = self.0.iter();
if let Some(byte) = bytes.next() {
f.write_fmt(format_args!("{byte:02x}"))?;
}
for byte in bytes {
f.write_fmt(format_args!(" {byte:02x}"))?;
}
f.write_char(']')
}
}
impl Visit for fmt::DebugStruct<'_, '_> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
self.field(field.name(), value);
}
}
impl Visit for fmt::DebugMap<'_, '_> {
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
self.entry(&format_args!("{}", field), value);
}
}
impl<F> Visit for F
where
F: FnMut(&Field, &dyn fmt::Debug),
{
fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) {
(self)(field, value)
}
}
macro_rules! impl_values {
( $( $record:ident( $( $whatever:tt)+ ) ),+ ) => {
$(
impl_value!{ $record( $( $whatever )+ ) }
)+
}
}
macro_rules! ty_to_nonzero {
(u8) => {
NonZeroU8
};
(u16) => {
NonZeroU16
};
(u32) => {
NonZeroU32
};
(u64) => {
NonZeroU64
};
(u128) => {
NonZeroU128
};
(usize) => {
NonZeroUsize
};
(i8) => {
NonZeroI8
};
(i16) => {
NonZeroI16
};
(i32) => {
NonZeroI32
};
(i64) => {
NonZeroI64
};
(i128) => {
NonZeroI128
};
(isize) => {
NonZeroIsize
};
}
macro_rules! impl_one_value {
(f32, $op:expr, $record:ident) => {
impl_one_value!(normal, f32, $op, $record);
};
(f64, $op:expr, $record:ident) => {
impl_one_value!(normal, f64, $op, $record);
};
(bool, $op:expr, $record:ident) => {
impl_one_value!(normal, bool, $op, $record);
};
($value_ty:tt, $op:expr, $record:ident) => {
impl_one_value!(normal, $value_ty, $op, $record);
impl_one_value!(nonzero, $value_ty, $op, $record);
};
(normal, $value_ty:tt, $op:expr, $record:ident) => {
impl $crate::sealed::Sealed for $value_ty {}
impl $crate::field::Value for $value_ty {
fn record(&self, key: &$crate::field::Field, visitor: &mut dyn $crate::field::Visit) {
#[allow(clippy::redundant_closure_call)]
visitor.$record(key, $op(*self))
}
}
};
(nonzero, $value_ty:tt, $op:expr, $record:ident) => {
#[allow(clippy::useless_attribute, unused)]
use num::*;
impl $crate::sealed::Sealed for ty_to_nonzero!($value_ty) {}
impl $crate::field::Value for ty_to_nonzero!($value_ty) {
fn record(&self, key: &$crate::field::Field, visitor: &mut dyn $crate::field::Visit) {
#[allow(clippy::redundant_closure_call)]
visitor.$record(key, $op(self.get()))
}
}
};
}
macro_rules! impl_value {
( $record:ident( $( $value_ty:tt ),+ ) ) => {
$(
impl_one_value!($value_ty, |this: $value_ty| this, $record);
)+
};
( $record:ident( $( $value_ty:tt ),+ as $as_ty:ty) ) => {
$(
impl_one_value!($value_ty, |this: $value_ty| this as $as_ty, $record);
)+
};
}
impl_values! {
record_u64(u64),
record_u64(usize, u32, u16, u8 as u64),
record_i64(i64),
record_i64(isize, i32, i16, i8 as i64),
record_u128(u128),
record_i128(i128),
record_bool(bool),
record_f64(f64, f32 as f64)
}
impl<T: crate::sealed::Sealed> crate::sealed::Sealed for Wrapping<T> {}
impl<T: crate::field::Value> crate::field::Value for Wrapping<T> {
fn record(&self, key: &crate::field::Field, visitor: &mut dyn crate::field::Visit) {
self.0.record(key, visitor)
}
}
impl crate::sealed::Sealed for str {}
impl Value for str {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_str(key, self)
}
}
impl crate::sealed::Sealed for [u8] {}
impl Value for [u8] {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_bytes(key, self)
}
}
#[cfg(feature = "std")]
impl crate::sealed::Sealed for dyn std::error::Error + 'static {}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl Value for dyn std::error::Error + 'static {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_error(key, self)
}
}
#[cfg(feature = "std")]
impl crate::sealed::Sealed for dyn std::error::Error + Send + 'static {}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl Value for dyn std::error::Error + Send + 'static {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
(self as &dyn std::error::Error).record(key, visitor)
}
}
#[cfg(feature = "std")]
impl crate::sealed::Sealed for dyn std::error::Error + Sync + 'static {}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl Value for dyn std::error::Error + Sync + 'static {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
(self as &dyn std::error::Error).record(key, visitor)
}
}
#[cfg(feature = "std")]
impl crate::sealed::Sealed for dyn std::error::Error + Send + Sync + 'static {}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl Value for dyn std::error::Error + Send + Sync + 'static {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
(self as &dyn std::error::Error).record(key, visitor)
}
}
impl<'a, T: ?Sized> crate::sealed::Sealed for &'a T where T: Value + crate::sealed::Sealed + 'a {}
impl<'a, T: ?Sized> Value for &'a T
where
T: Value + 'a,
{
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
(*self).record(key, visitor)
}
}
impl<'a, T: ?Sized> crate::sealed::Sealed for &'a mut T where T: Value + crate::sealed::Sealed + 'a {}
impl<'a, T: ?Sized> Value for &'a mut T
where
T: Value + 'a,
{
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
T::record(self, key, visitor)
}
}
impl crate::sealed::Sealed for fmt::Arguments<'_> {}
impl Value for fmt::Arguments<'_> {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_debug(key, self)
}
}
impl<T: ?Sized> crate::sealed::Sealed for Box<T> where T: Value {}
impl<T: ?Sized> Value for Box<T>
where
T: Value,
{
#[inline]
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
self.as_ref().record(key, visitor)
}
}
impl crate::sealed::Sealed for String {}
impl Value for String {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_str(key, self.as_str())
}
}
impl fmt::Debug for dyn Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct NullCallsite;
static NULL_CALLSITE: NullCallsite = NullCallsite;
impl crate::callsite::Callsite for NullCallsite {
fn set_interest(&self, _: crate::subscriber::Interest) {
unreachable!("you somehow managed to register the null callsite?")
}
fn metadata(&self) -> &crate::Metadata<'_> {
unreachable!("you somehow managed to access the null callsite?")
}
}
static FIELD: Field = Field {
i: 0,
fields: FieldSet::new(&[], crate::identify_callsite!(&NULL_CALLSITE)),
};
let mut res = Ok(());
self.record(&FIELD, &mut |_: &Field, val: &dyn fmt::Debug| {
res = write!(f, "{:?}", val);
});
res
}
}
impl fmt::Display for dyn Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T: fmt::Display> crate::sealed::Sealed for DisplayValue<T> {}
impl<T> Value for DisplayValue<T>
where
T: fmt::Display,
{
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_debug(key, self)
}
}
impl<T: fmt::Display> fmt::Debug for DisplayValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<T: fmt::Display> fmt::Display for DisplayValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<T: fmt::Debug> crate::sealed::Sealed for DebugValue<T> {}
impl<T> Value for DebugValue<T>
where
T: fmt::Debug,
{
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_debug(key, &self.0)
}
}
impl<T: fmt::Debug> fmt::Debug for DebugValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(all(tracing_unstable, feature = "valuable"))]
impl crate::sealed::Sealed for valuable::Value<'_> {}
#[cfg(all(tracing_unstable, feature = "valuable"))]
#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
impl Value for valuable::Value<'_> {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_value(key, *self)
}
}
#[cfg(all(tracing_unstable, feature = "valuable"))]
impl crate::sealed::Sealed for &'_ dyn valuable::Valuable {}
#[cfg(all(tracing_unstable, feature = "valuable"))]
#[cfg_attr(docsrs, doc(cfg(all(tracing_unstable, feature = "valuable"))))]
impl Value for &'_ dyn valuable::Valuable {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
visitor.record_value(key, self.as_value())
}
}
impl crate::sealed::Sealed for Empty {}
impl Value for Empty {
#[inline]
fn record(&self, _: &Field, _: &mut dyn Visit) {}
}
impl<T: Value> crate::sealed::Sealed for Option<T> {}
impl<T: Value> Value for Option<T> {
fn record(&self, key: &Field, visitor: &mut dyn Visit) {
if let Some(v) = &self {
v.record(key, visitor)
}
}
}
impl Field {
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
pub fn name(&self) -> &'static str {
self.fields.names[self.i]
}
pub fn index(&self) -> usize {
self.i
}
}
impl fmt::Display for Field {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.pad(self.name())
}
}
impl AsRef<str> for Field {
fn as_ref(&self) -> &str {
self.name()
}
}
impl PartialEq for Field {
fn eq(&self, other: &Self) -> bool {
self.callsite() == other.callsite() && self.i == other.i
}
}
impl Eq for Field {}
impl Hash for Field {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
self.callsite().hash(state);
self.i.hash(state);
}
}
impl Clone for Field {
fn clone(&self) -> Self {
Field {
i: self.i,
fields: FieldSet {
names: self.fields.names,
callsite: self.fields.callsite(),
},
}
}
}
impl FieldSet {
pub const fn new(names: &'static [&'static str], callsite: callsite::Identifier) -> Self {
Self { names, callsite }
}
#[inline]
pub(crate) const fn callsite(&self) -> callsite::Identifier {
callsite::Identifier(self.callsite.0)
}
pub fn field<Q: Borrow<str> + ?Sized>(&self, name: &Q) -> Option<Field> {
let name = &name.borrow();
self.names.iter().position(|f| f == name).map(|i| Field {
i,
fields: FieldSet {
names: self.names,
callsite: self.callsite(),
},
})
}
pub fn contains(&self, field: &Field) -> bool {
field.callsite() == self.callsite() && field.i <= self.len()
}
#[inline]
pub fn iter(&self) -> Iter {
let idxs = 0..self.len();
Iter {
idxs,
fields: FieldSet {
names: self.names,
callsite: self.callsite(),
},
}
}
#[doc(hidden)]
pub fn value_set<'v, V>(&'v self, values: &'v V) -> ValueSet<'v>
where
V: ValidLen<'v>,
{
ValueSet {
fields: self,
values: Values::Explicit(values.borrow()),
}
}
#[doc(hidden)]
pub fn value_set_all<'v>(&'v self, values: &'v [Option<&'v (dyn Value + 'v)>]) -> ValueSet<'v> {
debug_assert_eq!(values.len(), self.len());
ValueSet {
fields: self,
values: Values::All(values),
}
}
pub(crate) const fn fake_field(&self) -> Field {
Field {
i: usize::MAX,
fields: FieldSet {
names: self.names,
callsite: self.callsite(),
},
}
}
#[inline]
pub fn len(&self) -> usize {
self.names.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.names.is_empty()
}
}
impl IntoIterator for &FieldSet {
type IntoIter = Iter;
type Item = Field;
#[inline]
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Debug for FieldSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FieldSet")
.field("names", &self.names)
.field("callsite", &self.callsite)
.finish()
}
}
impl fmt::Display for FieldSet {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set()
.entries(self.names.iter().map(display))
.finish()
}
}
impl Eq for FieldSet {}
impl PartialEq for FieldSet {
fn eq(&self, other: &Self) -> bool {
if core::ptr::eq(&self, &other) {
true
} else if cfg!(not(debug_assertions)) {
self.callsite == other.callsite
} else {
let Self {
names: lhs_names,
callsite: lhs_callsite,
} = self;
let Self {
names: rhs_names,
callsite: rhs_callsite,
} = &other;
lhs_callsite == rhs_callsite && lhs_names == rhs_names
}
}
}
impl Iterator for Iter {
type Item = Field;
#[inline]
fn next(&mut self) -> Option<Field> {
let i = self.idxs.next()?;
Some(Field {
i,
fields: FieldSet {
names: self.fields.names,
callsite: self.fields.callsite(),
},
})
}
}
impl ValueSet<'_> {
#[inline]
pub fn callsite(&self) -> callsite::Identifier {
self.fields.callsite()
}
pub fn record(&self, visitor: &mut dyn Visit) {
match self.values {
Values::Explicit(values) => {
let my_callsite = self.callsite();
for (field, value) in values {
if field.callsite() != my_callsite {
continue;
}
if let Some(value) = *value {
value.record(field, visitor);
}
}
}
Values::All(values) => {
for (field, value) in self.fields.iter().zip(values.iter()) {
if let Some(value) = *value {
value.record(&field, visitor);
}
}
}
}
}
pub fn len(&self) -> usize {
match self.values {
Values::Explicit(values) => {
let my_callsite = self.callsite();
values
.iter()
.filter(|(field, _)| field.callsite() == my_callsite)
.count()
}
Values::All(values) => values.len(),
}
}
pub(crate) fn contains(&self, field: &Field) -> bool {
if field.callsite() != self.callsite() {
return false;
}
match self.values {
Values::Explicit(values) => values
.iter()
.any(|(key, val)| *key == field && val.is_some()),
Values::All(values) => values[field.i].is_some(),
}
}
pub fn is_empty(&self) -> bool {
match self.values {
Values::All(values) => values.iter().all(|v| v.is_none()),
Values::Explicit(values) => {
let my_callsite = self.callsite();
values
.iter()
.all(|(key, val)| val.is_none() || key.callsite() != my_callsite)
}
}
}
pub(crate) fn field_set(&self) -> &FieldSet {
self.fields
}
}
impl fmt::Debug for ValueSet<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("ValueSet");
self.record(&mut s);
s.field("callsite", &self.callsite()).finish()
}
}
impl fmt::Display for ValueSet<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_map();
self.record(&mut s);
s.finish()
}
}
mod private {
use super::*;
pub trait ValidLen<'a>: Borrow<[(&'a Field, Option<&'a (dyn Value + 'a)>)]> {}
impl<'a, const N: usize> ValidLen<'a> for [(&'a Field, Option<&'a (dyn Value + 'a)>); N] {}
}
#[cfg(test)]
mod test {
use alloc::{borrow::ToOwned, boxed::Box, string::String};
use std::format;
use super::*;
use crate::metadata::{Kind, Level, Metadata};
struct TestCallsite1 {
_unused: u8,
}
static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1 { _unused: 0 };
static TEST_META_1: Metadata<'static> = metadata! {
name: "field_test1",
target: module_path!(),
level: Level::INFO,
fields: &["foo", "bar", "baz"],
callsite: &TEST_CALLSITE_1,
kind: Kind::SPAN,
};
impl crate::callsite::Callsite for TestCallsite1 {
fn set_interest(&self, _: crate::subscriber::Interest) {
unimplemented!()
}
fn metadata(&self) -> &Metadata<'_> {
&TEST_META_1
}
}
struct TestCallsite2 {
_unused: u8,
}
static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2 { _unused: 0 };
static TEST_META_2: Metadata<'static> = metadata! {
name: "field_test2",
target: module_path!(),
level: Level::INFO,
fields: &["foo", "bar", "baz"],
callsite: &TEST_CALLSITE_2,
kind: Kind::SPAN,
};
impl crate::callsite::Callsite for TestCallsite2 {
fn set_interest(&self, _: crate::subscriber::Interest) {
unimplemented!()
}
fn metadata(&self) -> &Metadata<'_> {
&TEST_META_2
}
}
#[test]
fn value_set_with_no_values_is_empty() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), None),
(&fields.field("bar").unwrap(), None),
(&fields.field("baz").unwrap(), None),
];
let valueset = fields.value_set(values);
assert!(valueset.is_empty());
}
#[test]
fn index_of_field_in_fieldset_is_correct() {
let fields = TEST_META_1.fields();
let foo = fields.field("foo").unwrap();
assert_eq!(foo.index(), 0);
let bar = fields.field("bar").unwrap();
assert_eq!(bar.index(), 1);
let baz = fields.field("baz").unwrap();
assert_eq!(baz.index(), 2);
}
#[test]
fn empty_value_set_is_empty() {
let fields = TEST_META_1.fields();
let valueset = fields.value_set(&[]);
assert!(valueset.is_empty());
}
#[test]
fn value_sets_with_fields_from_other_callsites_are_empty() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), Some(&1 as &dyn Value)),
(&fields.field("bar").unwrap(), Some(&2 as &dyn Value)),
(&fields.field("baz").unwrap(), Some(&3 as &dyn Value)),
];
let valueset = TEST_META_2.fields().value_set(values);
assert!(valueset.is_empty())
}
#[test]
fn sparse_value_sets_are_not_empty() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), None),
(&fields.field("bar").unwrap(), Some(&57 as &dyn Value)),
(&fields.field("baz").unwrap(), None),
];
let valueset = fields.value_set(values);
assert!(!valueset.is_empty());
}
#[test]
fn fields_from_other_callsets_are_skipped() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), None),
(
&TEST_META_2.fields().field("bar").unwrap(),
Some(&57 as &dyn Value),
),
(&fields.field("baz").unwrap(), None),
];
struct MyVisitor;
impl Visit for MyVisitor {
fn record_debug(&mut self, field: &Field, _: &dyn fmt::Debug) {
assert_eq!(field.callsite(), TEST_META_1.callsite())
}
}
let valueset = fields.value_set(values);
valueset.record(&mut MyVisitor);
}
#[test]
fn empty_fields_are_skipped() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), Some(&Empty as &dyn Value)),
(&fields.field("bar").unwrap(), Some(&57 as &dyn Value)),
(&fields.field("baz").unwrap(), Some(&Empty as &dyn Value)),
];
struct MyVisitor;
impl Visit for MyVisitor {
fn record_debug(&mut self, field: &Field, _: &dyn fmt::Debug) {
assert_eq!(field.name(), "bar")
}
}
let valueset = fields.value_set(values);
valueset.record(&mut MyVisitor);
}
#[test]
fn record_debug_fn() {
let fields = TEST_META_1.fields();
let values = &[
(&fields.field("foo").unwrap(), Some(&1 as &dyn Value)),
(&fields.field("bar").unwrap(), Some(&2 as &dyn Value)),
(&fields.field("baz").unwrap(), Some(&3 as &dyn Value)),
];
let valueset = fields.value_set(values);
let mut result = String::new();
valueset.record(&mut |_: &Field, value: &dyn fmt::Debug| {
use core::fmt::Write;
write!(&mut result, "{:?}", value).unwrap();
});
assert_eq!(result, "123".to_owned());
}
#[test]
#[cfg(feature = "std")]
fn record_error() {
let fields = TEST_META_1.fields();
let err: Box<dyn std::error::Error + Send + Sync + 'static> =
std::io::Error::new(std::io::ErrorKind::Other, "lol").into();
let values = &[
(&fields.field("foo").unwrap(), Some(&err as &dyn Value)),
(&fields.field("bar").unwrap(), Some(&Empty as &dyn Value)),
(&fields.field("baz").unwrap(), Some(&Empty as &dyn Value)),
];
let valueset = fields.value_set(values);
let mut result = String::new();
valueset.record(&mut |_: &Field, value: &dyn fmt::Debug| {
use core::fmt::Write;
write!(&mut result, "{:?}", value).unwrap();
});
assert_eq!(result, format!("{}", err));
}
#[test]
fn record_bytes() {
let fields = TEST_META_1.fields();
let first = &b"abc"[..];
let second: &[u8] = &[192, 255, 238];
let values = &[
(&fields.field("foo").unwrap(), Some(&first as &dyn Value)),
(&fields.field("bar").unwrap(), Some(&" " as &dyn Value)),
(&fields.field("baz").unwrap(), Some(&second as &dyn Value)),
];
let valueset = fields.value_set(values);
let mut result = String::new();
valueset.record(&mut |_: &Field, value: &dyn fmt::Debug| {
use core::fmt::Write;
write!(&mut result, "{:?}", value).unwrap();
});
assert_eq!(result, format!("{}", r#"[61 62 63]" "[c0 ff ee]"#));
}
}