From dbc0a3f1ebe53cdf87dfd68ff313f95294508d97 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 26 Jan 2024 18:03:14 +1000 Subject: [PATCH 01/26] start filling in docs and remove capture/downcast value support --- src/kv/key.rs | 2 +- src/kv/mod.rs | 56 +++++++++++++++ src/kv/source.rs | 26 ++++--- src/kv/value.rs | 172 ++--------------------------------------------- 4 files changed, 80 insertions(+), 176 deletions(-) diff --git a/src/kv/key.rs b/src/kv/key.rs index 858ec493a..e53a64a6f 100644 --- a/src/kv/key.rs +++ b/src/kv/key.rs @@ -30,7 +30,7 @@ impl ToKey for str { } } -/// A key in a structured key-value pair. +/// A key in a user-defined attribute. // These impls must only be based on the as_str() representation of the key // If a new field (such as an optional index) is added to the key they must not affect comparison #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 5dc69337c..402c34585 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -11,6 +11,62 @@ //! [dependencies.log] //! features = ["kv_unstable"] //! ``` +//! +//! # Structured logging in `log` +//! +//! Structured logging enhances traditional text-based log records with user-defined +//! attributes. Structured logs can be analyzed using a variety of tranditional +//! data processing techniques, without needing to find and parse attributes from +//! unstructured text first. +//! +//! In `log`, user-defined attributes are part of a [`Source`] on the [`LogRecord`]. +//! Each attribute is a pair of [`Key`] and [`Value`]. Keys are strings and values +//! are a datum of any type that can be formatted or serialized. Simple types like +//! strings, booleans, and numbers are supported, as well as arbitrarily complex +//! structures involving nested objects and sequences. +//! +//! ## Adding attributes to log records +//! +//! Attributes appear after the message format in the `log!` macros: +//! +//! ``` +//! .. +//! ``` +//! +//! ## Working with attributes on log records +//! +//! Use the [`LogRecord::source`] method to access user-defined attributes. +//! Individual attributes can be pulled from the source: +//! +//! ``` +//! .. +//! ``` +//! +//! This is convenient when an attribute of interest is known in advance. +//! All attributes can also be enumerated using a [`Visitor`]: +//! +//! ``` +//! .. +//! ``` +//! +//! [`Value`]s in attributes have methods for conversions to common types: +//! +//! ``` +//! .. +//! ``` +//! +//! Values also have their own [`value::Visitor`] type: +//! +//! ``` +//! .. +//! ``` +//! +//! Visitors on values are lightweight and suitable for detecting primitive types. +//! To serialize a value, you can also use either `serde` or `sval`. If you're +//! in a no-std environment, you can use `sval`. In other cases, you can use `serde`. +//! +//! Values can also always be formatted using the standard `Debug` and `Display` +//! traits. mod error; mod key; diff --git a/src/kv/source.rs b/src/kv/source.rs index 9c56f8b76..510d41acd 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -1,23 +1,31 @@ -//! Sources for key-value pairs. +//! Sources for user-defined attributes. use crate::kv::{Error, Key, ToKey, ToValue, Value}; use std::fmt; -/// A source of key-value pairs. +/// A source of user-defined attributes. /// /// The source may be a single pair, a set of pairs, or a filter over a set of pairs. /// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data /// in a source. +/// +/// # Examples +/// +/// Enumerating the attributes in a source: +/// +/// ``` +/// .. +/// ``` pub trait Source { - /// Visit key-value pairs. + /// Visit attributes. /// - /// A source doesn't have to guarantee any ordering or uniqueness of key-value pairs. + /// A source doesn't have to guarantee any ordering or uniqueness of attributes. /// If the given visitor returns an error then the source may early-return with it, - /// even if there are more key-value pairs. + /// even if there are more attributes. /// /// # Implementation notes /// - /// A source should yield the same key-value pairs to a subsequent visitor unless + /// A source should yield the same attributes to a subsequent visitor unless /// that visitor itself fails. fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; @@ -34,14 +42,14 @@ pub trait Source { get_default(self, key) } - /// Count the number of key-value pairs that can be visited. + /// Count the number of attributes that can be visited. /// /// # Implementation notes /// - /// A source that knows the number of key-value pairs upfront may provide a more + /// A source that knows the number of attributes upfront may provide a more /// efficient implementation. /// - /// A subsequent call to `visit` should yield the same number of key-value pairs + /// A subsequent call to `visit` should yield the same number of attributes /// to the visitor, unless that visitor fails part way through. fn count(&self) -> usize { count_default(self) diff --git a/src/kv/value.rs b/src/kv/value.rs index 1c39bef0a..7b0f71e9d 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -29,73 +29,18 @@ impl<'v> ToValue for Value<'v> { } } -/// Get a value from a type implementing `std::fmt::Debug`. -#[macro_export] -macro_rules! as_debug { - ($capture:expr) => { - $crate::kv::Value::from_debug(&$capture) - }; -} - -/// Get a value from a type implementing `std::fmt::Display`. -#[macro_export] -macro_rules! as_display { - ($capture:expr) => { - $crate::kv::Value::from_display(&$capture) - }; -} - -/// Get a value from an error. -#[cfg(feature = "kv_unstable_std")] -#[macro_export] -macro_rules! as_error { - ($capture:expr) => { - $crate::kv::Value::from_dyn_error(&$capture) - }; -} - -#[cfg(feature = "kv_unstable_serde")] -/// Get a value from a type implementing `serde::Serialize`. -#[macro_export] -macro_rules! as_serde { - ($capture:expr) => { - $crate::kv::Value::from_serde(&$capture) - }; -} - -/// Get a value from a type implementing `sval::Value`. -#[cfg(feature = "kv_unstable_sval")] -#[macro_export] -macro_rules! as_sval { - ($capture:expr) => { - $crate::kv::Value::from_sval(&$capture) - }; -} - -/// A value in a structured key-value pair. +/// A value in a user-defined attribute. +/// +/// Values are an anonymous bag containing some structured datum. /// /// # Capturing values /// /// There are a few ways to capture a value: /// -/// - Using the `Value::capture_*` methods. /// - Using the `Value::from_*` methods. /// - Using the `ToValue` trait. /// - Using the standard `From` trait. /// -/// ## Using the `Value::capture_*` methods -/// -/// `Value` offers a few constructor methods that capture values of different kinds. -/// These methods require a `T: 'static` to support downcasting. -/// -/// ``` -/// use log::kv::Value; -/// -/// let value = Value::capture_debug(&42i32); -/// -/// assert_eq!(Some(42), value.to_i64()); -/// ``` -/// /// ## Using the `Value::from_*` methods /// /// `Value` offers a few constructor methods that capture values of different kinds. @@ -157,59 +102,6 @@ impl<'v> Value<'v> { value.to_value() } - /// Get a value from a type implementing `std::fmt::Debug`. - pub fn capture_debug(value: &'v T) -> Self - where - T: fmt::Debug + 'static, - { - Value { - inner: ValueBag::capture_debug(value), - } - } - - /// Get a value from a type implementing `std::fmt::Display`. - pub fn capture_display(value: &'v T) -> Self - where - T: fmt::Display + 'static, - { - Value { - inner: ValueBag::capture_display(value), - } - } - - /// Get a value from an error. - #[cfg(feature = "kv_unstable_std")] - pub fn capture_error(err: &'v T) -> Self - where - T: std::error::Error + 'static, - { - Value { - inner: ValueBag::capture_error(err), - } - } - - #[cfg(feature = "kv_unstable_serde")] - /// Get a value from a type implementing `serde::Serialize`. - pub fn capture_serde(value: &'v T) -> Self - where - T: serde::Serialize + 'static, - { - Value { - inner: ValueBag::capture_serde1(value), - } - } - - /// Get a value from a type implementing `sval::Value`. - #[cfg(feature = "kv_unstable_sval")] - pub fn capture_sval(value: &'v T) -> Self - where - T: sval::Value + 'static, - { - Value { - inner: ValueBag::capture_sval2(value), - } - } - /// Get a value from a type implementing `std::fmt::Debug`. pub fn from_debug(value: &'v T) -> Self where @@ -284,17 +176,10 @@ impl<'v> Value<'v> { } } - /// Check whether this value can be downcast to `T`. - pub fn is(&self) -> bool { - self.inner.is::() - } - - /// Try downcast this value to `T`. - pub fn downcast_ref(&self) -> Option<&T> { - self.inner.downcast_ref::() - } - /// Inspect this value using a simple visitor. + /// + /// When the `kv_unstable_serde` or `kv_unstable_sval` features are enabled, you can also + /// serialize a value using its `Serialize` or `Value` implementation. pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> { struct Visitor(V); @@ -824,40 +709,6 @@ pub(crate) mod tests { vec![Value::from('a'), Value::from('⛰')].into_iter() } - #[test] - fn test_capture_fmt() { - assert_eq!(Some(42u64), Value::capture_display(&42).to_u64()); - assert_eq!(Some(42u64), Value::capture_debug(&42).to_u64()); - - assert!(Value::from_display(&42).to_u64().is_none()); - assert!(Value::from_debug(&42).to_u64().is_none()); - } - - #[cfg(feature = "kv_unstable_std")] - #[test] - fn test_capture_error() { - let err = std::io::Error::from(std::io::ErrorKind::Other); - - assert!(Value::capture_error(&err).to_borrowed_error().is_some()); - assert!(Value::from_dyn_error(&err).to_borrowed_error().is_some()); - } - - #[cfg(feature = "kv_unstable_serde")] - #[test] - fn test_capture_serde() { - assert_eq!(Some(42u64), Value::capture_serde(&42).to_u64()); - - assert_eq!(Some(42u64), Value::from_serde(&42).to_u64()); - } - - #[cfg(feature = "kv_unstable_sval")] - #[test] - fn test_capture_sval() { - assert_eq!(Some(42u64), Value::capture_sval(&42).to_u64()); - - assert_eq!(Some(42u64), Value::from_sval(&42).to_u64()); - } - #[test] fn test_to_value_display() { assert_eq!(42u64.to_value().to_string(), "42"); @@ -966,17 +817,6 @@ pub(crate) mod tests { } } - #[test] - fn test_downcast_ref() { - #[derive(Debug)] - struct Foo(u64); - - let v = Value::capture_debug(&Foo(42)); - - assert!(v.is::()); - assert_eq!(42u64, v.downcast_ref::().expect("invalid downcast").0); - } - #[test] fn test_visit_integer() { struct Extract(Option); From fdc1c6e0b8e1a7f5d385d1b77a10b52dd24248b0 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 29 Jan 2024 08:20:39 +1000 Subject: [PATCH 02/26] rename value::Visit to value::Visitor --- src/kv/mod.rs | 17 +++++++++++++---- src/kv/source.rs | 3 +++ src/kv/value.rs | 27 +++++++++++++++++++-------- src/lib.rs | 2 ++ 4 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 402c34585..deaf4615f 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -1,4 +1,4 @@ -//! **UNSTABLE:** Structured key-value pairs. +//! **UNSTABLE:** Structured logging. //! //! This module is unstable and breaking changes may be made //! at any time. See [the tracking issue](https://github.com/rust-lang-nursery/log/issues/328) @@ -62,11 +62,20 @@ //! ``` //! //! Visitors on values are lightweight and suitable for detecting primitive types. -//! To serialize a value, you can also use either `serde` or `sval`. If you're -//! in a no-std environment, you can use `sval`. In other cases, you can use `serde`. +//! To serialize a value, you can also use either `serde` or `sval`: +//! +//! ``` +//! .. +//! ``` +//! +//! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. //! //! Values can also always be formatted using the standard `Debug` and `Display` -//! traits. +//! traits: +//! +//! ``` +//! .. +//! ``` mod error; mod key; diff --git a/src/kv/source.rs b/src/kv/source.rs index 510d41acd..07e5eb706 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -1,4 +1,7 @@ //! Sources for user-defined attributes. +//! +//! This module defines the [`Source`] type and supporting APIs for +//! working with collections of attributes. use crate::kv::{Error, Key, ToKey, ToValue, Value}; use std::fmt; diff --git a/src/kv/value.rs b/src/kv/value.rs index 7b0f71e9d..e1f0cb117 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -1,4 +1,7 @@ //! Structured values. +//! +//! This module defines the [`Value`] type and supporting APIs for +//! capturing and serializing them. use std::fmt; @@ -89,6 +92,14 @@ impl<'v> ToValue for Value<'v> { /// For more complex types one of the following traits can be used: /// * [`sval::Value`], requires the `kv_unstable_sval` feature. /// * [`serde::Serialize`], requires the `kv_unstable_serde` feature. +/// +/// You don't need a [`Visit`] to serialize values. +/// +/// A value can always be serialized using any supported framework, regardless +/// of how it was captured. If, for example, a value was captured using its +/// `Display` implementation, it will serialize as a string. If it was captured +/// as a struct using its `serde::Serialize`, it will also serialize as a struct +/// through `sval`, or be formatted using a `Debug`-compatible representation. pub struct Value<'v> { inner: ValueBag<'v>, } @@ -180,12 +191,12 @@ impl<'v> Value<'v> { /// /// When the `kv_unstable_serde` or `kv_unstable_sval` features are enabled, you can also /// serialize a value using its `Serialize` or `Value` implementation. - pub fn visit(&self, visitor: impl Visit<'v>) -> Result<(), Error> { + pub fn visit(&self, visitor: impl Visitor<'v>) -> Result<(), Error> { struct Visitor(V); - impl<'v, V> value_bag::visit::Visit<'v> for Visitor + impl<'v, V> value_bag::visit::Visitor<'v> for Visitor where - V: Visit<'v>, + V: Visitor<'v>, { fn visit_any(&mut self, value: ValueBag) -> Result<(), value_bag::Error> { self.0 @@ -521,7 +532,7 @@ mod std_support { /// Also see [`Value`'s documentation on seralization]. /// /// [`Value`'s documentation on seralization]: Value#serialization -pub trait Visit<'v> { +pub trait Visitor<'v> { /// Visit a `Value`. /// /// This is the only required method on `Visit` and acts as a fallback for any @@ -592,9 +603,9 @@ pub trait Visit<'v> { } } -impl<'a, 'v, T: ?Sized> Visit<'v> for &'a mut T +impl<'a, 'v, T: ?Sized> Visitor<'v> for &'a mut T where - T: Visit<'v>, + T: Visitor<'v>, { fn visit_any(&mut self, value: Value) -> Result<(), Error> { (**self).visit_any(value) @@ -821,7 +832,7 @@ pub(crate) mod tests { fn test_visit_integer() { struct Extract(Option); - impl<'v> Visit<'v> for Extract { + impl<'v> Visitor<'v> for Extract { fn visit_any(&mut self, value: Value) -> Result<(), Error> { unimplemented!("unexpected value: {value:?}") } @@ -843,7 +854,7 @@ pub(crate) mod tests { fn test_visit_borrowed_str() { struct Extract<'v>(Option<&'v str>); - impl<'v> Visit<'v> for Extract<'v> { + impl<'v> Visitor<'v> for Extract<'v> { fn visit_any(&mut self, value: Value) -> Result<(), Error> { unimplemented!("unexpected value: {value:?}") } diff --git a/src/lib.rs b/src/lib.rs index 6a3a8ca98..6a8e57762 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,6 +121,8 @@ //! # #[cfg(not(feature = "kv_unstable_serde"))] //! # fn main() {} //! ``` +//! +//! See the [`kv`] module documentation for more details. //! //! # Available logging implementations //! From 7f93acaae99f4defbce9a6bbd0b52015e4ca24dd Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 29 Jan 2024 15:56:35 +1000 Subject: [PATCH 03/26] start factoring out value-bag --- Cargo.toml | 2 +- src/kv/source.rs | 14 ++-- src/kv/value.rs | 211 +++++++++++++++++++++++++++-------------------- 3 files changed, 129 insertions(+), 98 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89012be02..99192dc5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ std = [] # requires the latest stable # this will have a tighter MSRV before stabilization -kv_unstable = ["value-bag"] +kv_unstable = [] kv_unstable_sval = ["kv_unstable", "value-bag/sval", "sval", "sval_ref"] kv_unstable_std = ["std", "kv_unstable", "value-bag/error"] kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"] diff --git a/src/kv/source.rs b/src/kv/source.rs index 07e5eb706..2f62e1999 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -363,7 +363,7 @@ mod std_support { mod tests { use std::collections::{BTreeMap, HashMap}; - use crate::kv::value::tests::Token; + use crate::kv::value; use super::*; @@ -377,7 +377,7 @@ mod std_support { fn get() { let source = vec![("a", 1), ("b", 2), ("a", 1)]; assert_eq!( - Token::I64(1), + value::inner::Token::I64(1), Source::get(&source, Key::from_str("a")).unwrap().to_token() ); @@ -393,7 +393,7 @@ mod std_support { assert_eq!(2, Source::count(&map)); assert_eq!( - Token::I64(1), + value::inner::Token::I64(1), Source::get(&map, Key::from_str("a")).unwrap().to_token() ); } @@ -406,7 +406,7 @@ mod std_support { assert_eq!(2, Source::count(&map)); assert_eq!( - Token::I64(1), + value::inner::Token::I64(1), Source::get(&map, Key::from_str("a")).unwrap().to_token() ); } @@ -415,7 +415,7 @@ mod std_support { #[cfg(test)] mod tests { - use crate::kv::value::tests::Token; + use crate::kv::value; use super::*; @@ -452,11 +452,11 @@ mod tests { fn get() { let source = &[("a", 1), ("b", 2), ("a", 1)] as &[_]; assert_eq!( - Token::I64(1), + value::inner::Token::I64(1), Source::get(source, Key::from_str("a")).unwrap().to_token() ); assert_eq!( - Token::I64(2), + value::inner::Token::I64(2), Source::get(source, Key::from_str("b")).unwrap().to_token() ); assert!(Source::get(&source, Key::from_str("c")).is_none()); diff --git a/src/kv/value.rs b/src/kv/value.rs index e1f0cb117..5b348f47f 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -7,8 +7,6 @@ use std::fmt; pub use crate::kv::Error; -use value_bag::ValueBag; - /// A type that can be converted into a [`Value`](struct.Value.html). pub trait ToValue { /// Perform the conversion. @@ -101,7 +99,7 @@ impl<'v> ToValue for Value<'v> { /// as a struct using its `serde::Serialize`, it will also serialize as a struct /// through `sval`, or be formatted using a `Debug`-compatible representation. pub struct Value<'v> { - inner: ValueBag<'v>, + inner: inner::Inner<'v>, } impl<'v> Value<'v> { @@ -119,7 +117,7 @@ impl<'v> Value<'v> { T: fmt::Debug, { Value { - inner: ValueBag::from_debug(value), + inner: inner::Inner::from_debug(value), } } @@ -129,7 +127,7 @@ impl<'v> Value<'v> { T: fmt::Display, { Value { - inner: ValueBag::from_display(value), + inner: inner::Inner::from_display(value), } } @@ -140,7 +138,7 @@ impl<'v> Value<'v> { T: serde::Serialize, { Value { - inner: ValueBag::from_serde1(value), + inner: inner::Inner::from_serde1(value), } } @@ -151,21 +149,21 @@ impl<'v> Value<'v> { T: sval::Value, { Value { - inner: ValueBag::from_sval2(value), + inner: inner::Inner::from_sval2(value), } } /// Get a value from a dynamic `std::fmt::Debug`. pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { Value { - inner: ValueBag::from_dyn_debug(value), + inner: inner::Inner::from_dyn_debug(value), } } /// Get a value from a dynamic `std::fmt::Display`. pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { Value { - inner: ValueBag::from_dyn_display(value), + inner: inner::Inner::from_dyn_display(value), } } @@ -173,14 +171,14 @@ impl<'v> Value<'v> { #[cfg(feature = "kv_unstable_std")] pub fn from_dyn_error(err: &'v (dyn std::error::Error + 'static)) -> Self { Value { - inner: ValueBag::from_dyn_error(err), + inner: inner::Inner::from_dyn_error(err), } } /// Get a value from an internal primitive. fn from_value_bag(value: T) -> Self where - T: Into>, + T: Into>, { Value { inner: value.into(), @@ -192,74 +190,7 @@ impl<'v> Value<'v> { /// When the `kv_unstable_serde` or `kv_unstable_sval` features are enabled, you can also /// serialize a value using its `Serialize` or `Value` implementation. pub fn visit(&self, visitor: impl Visitor<'v>) -> Result<(), Error> { - struct Visitor(V); - - impl<'v, V> value_bag::visit::Visitor<'v> for Visitor - where - V: Visitor<'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) + inner::visit(&self.inner, visitor) } } @@ -661,13 +592,113 @@ where } } +#[cfg(feature = "value-bag")] +pub(in crate::kv) mod inner { + use super::*; + + pub use value_bag::ValueBag as Inner; + + #[cfg(test)] + pub use value_bag::test::TestToken as Token; + + #[cfg(test)] + pub fn to_test_token<'v>(inner: &Inner<'v>) -> Token { + inner.to_test_token() + } + + pub fn visit<'v>(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), Error> { + struct InnerVisitor(V); + + impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitor + where + V: Visitor<'v>, + { + fn visit_any(&mut self, value: value_bag::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) + } + } + + inner + .visit(&mut InnerVisitor(visitor)) + .map_err(Error::from_value) + } +} + +#[cfg(not(feature = "value-bag"))] +pub(in crate::kv) mod inner { + use super::*; + + pub enum Inner<'v> { + + } + + pub enum Token {} + + pub fn visit(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), Error> { + todo!() + } +} + #[cfg(test)] pub(crate) mod tests { use super::*; - pub(crate) use value_bag::test::TestToken as Token; impl<'v> Value<'v> { - pub(crate) fn to_token(&self) -> Token { + pub(crate) fn to_token(&self) -> inner::Token { self.inner.to_test_token() } } @@ -735,18 +766,18 @@ pub(crate) mod tests { #[test] fn test_to_value_structured() { - assert_eq!(42u64.to_value().to_token(), Token::U64(42)); - assert_eq!(42i64.to_value().to_token(), Token::I64(42)); - assert_eq!(42.01f64.to_value().to_token(), Token::F64(42.01)); - assert_eq!(true.to_value().to_token(), Token::Bool(true)); - assert_eq!('a'.to_value().to_token(), Token::Char('a')); + assert_eq!(42u64.to_value().to_token(), inner::Token::U64(42)); + assert_eq!(42i64.to_value().to_token(), inner::Token::I64(42)); + assert_eq!(42.01f64.to_value().to_token(), inner::Token::F64(42.01)); + assert_eq!(true.to_value().to_token(), inner::Token::Bool(true)); + assert_eq!('a'.to_value().to_token(), inner::Token::Char('a')); assert_eq!( "a loong string".to_value().to_token(), - Token::Str("a loong string".into()) + inner::Token::Str("a loong string".into()) ); - assert_eq!(Some(true).to_value().to_token(), Token::Bool(true)); - assert_eq!(().to_value().to_token(), Token::None); - assert_eq!(None::.to_value().to_token(), Token::None); + assert_eq!(Some(true).to_value().to_token(), inner::Token::Bool(true)); + assert_eq!(().to_value().to_token(), inner::Token::None); + assert_eq!(None::.to_value().to_token(), inner::Token::None); } #[test] From e3a5b987b97513efb3a2699df176beb9e6a0c6cd Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 29 Jan 2024 16:25:09 +1000 Subject: [PATCH 04/26] stub out the internal value APIs --- src/kv/error.rs | 16 +-- src/kv/value.rs | 261 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 234 insertions(+), 43 deletions(-) diff --git a/src/kv/error.rs b/src/kv/error.rs index 9643a47f2..a6f48d0c6 100644 --- a/src/kv/error.rs +++ b/src/kv/error.rs @@ -1,5 +1,7 @@ use std::fmt; +use crate::kv::value; + /// An error encountered while working with structured data. #[derive(Debug)] pub struct Error { @@ -11,7 +13,7 @@ enum Inner { #[cfg(feature = "std")] Boxed(std_support::BoxedError), Msg(&'static str), - Value(value_bag::Error), + Value(value::inner::Error), Fmt, } @@ -23,21 +25,21 @@ impl Error { } } - // Not public so we don't leak the `value_bag` API - pub(super) fn from_value(err: value_bag::Error) -> Self { + // Not public so we don't leak the `value::inner` API + pub(super) fn from_value(err: value::inner::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 { + // Not public so we don't leak the `value::inner` API + pub(super) fn into_value(self) -> value::inner::Error { match self.inner { Inner::Value(err) => err, #[cfg(feature = "kv_unstable_std")] - _ => value_bag::Error::boxed(self), + _ => value::inner::Error::boxed(self), #[cfg(not(feature = "kv_unstable_std"))] - _ => value_bag::Error::msg("error inspecting a value"), + _ => value::inner::Error::msg("error inspecting a value"), } } } diff --git a/src/kv/value.rs b/src/kv/value.rs index 5b348f47f..854c0ebc0 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -176,7 +176,7 @@ impl<'v> Value<'v> { } /// Get a value from an internal primitive. - fn from_value_bag(value: T) -> Self + fn from_inner(value: T) -> Self where T: Into>, { @@ -262,39 +262,39 @@ impl ToValue for std::num::NonZeroI128 { impl<'v> From<&'v str> for Value<'v> { fn from(value: &'v str) -> Self { - Value::from_value_bag(value) + Value::from_inner(value) } } impl<'v> From<&'v u128> for Value<'v> { fn from(value: &'v u128) -> Self { - Value::from_value_bag(value) + Value::from_inner(value) } } impl<'v> From<&'v i128> for Value<'v> { fn from(value: &'v i128) -> Self { - Value::from_value_bag(value) + Value::from_inner(value) } } impl<'v> From<&'v std::num::NonZeroU128> for Value<'v> { fn from(v: &'v std::num::NonZeroU128) -> Value<'v> { // SAFETY: `NonZeroU128` and `u128` have the same ABI - Value::from_value_bag(unsafe { &*(v as *const std::num::NonZeroU128 as *const u128) }) + Value::from_inner(unsafe { &*(v as *const std::num::NonZeroU128 as *const u128) }) } } impl<'v> From<&'v std::num::NonZeroI128> for Value<'v> { fn from(v: &'v std::num::NonZeroI128) -> Value<'v> { // SAFETY: `NonZeroI128` and `i128` have the same ABI - Value::from_value_bag(unsafe { &*(v as *const std::num::NonZeroI128 as *const i128) }) + Value::from_inner(unsafe { &*(v as *const std::num::NonZeroI128 as *const i128) }) } } impl ToValue for () { fn to_value(&self) -> Value { - Value::from_value_bag(()) + Value::from_inner(()) } } @@ -305,7 +305,7 @@ where fn to_value(&self) -> Value { match *self { Some(ref value) => value.to_value(), - None => Value::from_value_bag(()), + None => Value::from_inner(()), } } } @@ -321,7 +321,7 @@ macro_rules! impl_to_value_primitive { impl<'v> From<$into_ty> for Value<'v> { fn from(value: $into_ty) -> Self { - Value::from_value_bag(value) + Value::from_inner(value) } } )* @@ -598,6 +598,8 @@ pub(in crate::kv) mod inner { pub use value_bag::ValueBag as Inner; + pub use value_bag::Error; + #[cfg(test)] pub use value_bag::test::TestToken as Token; @@ -606,75 +608,75 @@ pub(in crate::kv) mod inner { inner.to_test_token() } - pub fn visit<'v>(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), Error> { + pub fn visit<'v>(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { struct InnerVisitor(V); impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitor where V: Visitor<'v>, { - fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), value_bag::Error> { + fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), Error> { self.0 .visit_any(Value { inner: value }) - .map_err(Error::into_value) + .map_err(crate::kv::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_u64(&mut self, value: u64) -> Result<(), Error> { + self.0.visit_u64(value).map_err(crate::kv::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_i64(&mut self, value: i64) -> Result<(), Error> { + self.0.visit_i64(value).map_err(crate::kv::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_u128(&mut self, value: u128) -> Result<(), Error> { + self.0.visit_u128(value).map_err(crate::kv::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_i128(&mut self, value: i128) -> Result<(), Error> { + self.0.visit_i128(value).map_err(crate::kv::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_f64(&mut self, value: f64) -> Result<(), Error> { + self.0.visit_f64(value).map_err(crate::kv::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_bool(&mut self, value: bool) -> Result<(), Error> { + self.0.visit_bool(value).map_err(crate::kv::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_str(&mut self, value: &str) -> Result<(), Error> { + self.0.visit_str(value).map_err(crate::kv::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_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { + self.0.visit_borrowed_str(value).map_err(crate::kv::Error::into_value) } - fn visit_char(&mut self, value: char) -> Result<(), value_bag::Error> { - self.0.visit_char(value).map_err(Error::into_value) + fn visit_char(&mut self, value: char) -> Result<(), Error> { + self.0.visit_char(value).map_err(crate::kv::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) + ) -> Result<(), Error> { + self.0.visit_error(err).map_err(crate::kv::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) + ) -> Result<(), Error> { + self.0.visit_borrowed_error(err).map_err(crate::kv::Error::into_value) } } inner .visit(&mut InnerVisitor(visitor)) - .map_err(Error::from_value) + .map_err(crate::kv::Error::from_value) } } @@ -682,13 +684,200 @@ pub(in crate::kv) mod inner { pub(in crate::kv) mod inner { use super::*; + #[derive(Clone)] pub enum Inner<'v> { + Str(&'v str), + } + + impl<'v> From<()> for Inner<'v> { + fn from(v: ()) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: bool) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: char) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: f32) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: f64) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i8) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i16) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i32) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: i64) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: isize) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u8) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u16) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u32) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: u64) -> Self { + todo!() + } + } + + impl<'v> From for Inner<'v> { + fn from(v: usize) -> Self { + todo!() + } + } + + impl<'v> From<&'v i128> for Inner<'v> { + fn from(v: &'v i128) -> Self { + todo!() + } + } + + impl<'v> From<&'v u128> for Inner<'v> { + fn from(v: &'v u128) -> Self { + todo!() + } + } + + impl<'v> From<&'v str> for Inner<'v> { + fn from(v: &'v str) -> Self { + todo!() + } + } + + impl<'v> fmt::Debug for Inner<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + todo!() + } + } + impl<'v> fmt::Display for Inner<'v> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + todo!() + } } + impl<'v> Inner<'v> { + pub fn from_debug(value: &'v T) -> Self { + todo!() + } + + pub fn from_display(value: &'v T) -> Self { + todo!() + } + + pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { + todo!() + } + + pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { + todo!() + } + + pub fn to_bool(&self) -> Option { + todo!() + } + + pub fn to_char(&self) -> Option { + todo!() + } + + pub fn to_f64(&self) -> Option { + todo!() + } + + pub fn to_i64(&self) -> Option { + todo!() + } + + pub fn to_u64(&self) -> Option { + todo!() + } + + pub fn to_u128(&self) -> Option { + todo!() + } + + pub fn to_i128(&self) -> Option { + todo!() + } + + pub fn to_borrowed_str(&self) -> Option<&'v str> { + todo!() + } + } + + #[derive(Debug, PartialEq, Eq)] pub enum Token {} - pub fn visit(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), Error> { + #[derive(Debug)] + pub struct Error {} + + impl Error { + pub fn msg(msg: &'static str) -> Self { + todo!() + } + } + + impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + todo!() + } + } + + pub fn visit<'v>(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { todo!() } } From 9fae53d74eb437eb12e95116d32e4fdf427eb56e Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 29 Jan 2024 16:30:27 +1000 Subject: [PATCH 05/26] fill in the test token API --- src/kv/value.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/kv/value.rs b/src/kv/value.rs index 854c0ebc0..67b5427ea 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -857,10 +857,24 @@ pub(in crate::kv) mod inner { pub fn to_borrowed_str(&self) -> Option<&'v str> { todo!() } + + #[cfg(test)] + pub fn to_test_token(&self) -> Token { + todo!() + } } - #[derive(Debug, PartialEq, Eq)] - pub enum Token {} + #[derive(Debug, PartialEq)] + pub enum Token<'v> { + None, + Bool(bool), + Char(char), + Str(&'v str), + F64(f64), + I64(i64), + U64(u64), + + } #[derive(Debug)] pub struct Error {} From 05119e1886b92b886e945b890b029e522d69fcea Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 29 Jan 2024 20:45:17 +1000 Subject: [PATCH 06/26] fill in some more doc examples --- Cargo.toml | 1 + src/kv/key.rs | 2 +- src/kv/mod.rs | 166 +++++++++++++++++++++++++++++++++++++++++------ src/kv/source.rs | 70 ++++++++++++++++---- src/kv/value.rs | 2 +- 5 files changed, 206 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 99192dc5d..ef0e3eb7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ value-bag = { version = "1.4", optional = true, default-features = false } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" serde_test = "1.0" sval = { version = "2.1" } sval_derive = { version = "2.1" } diff --git a/src/kv/key.rs b/src/kv/key.rs index e53a64a6f..fb58008c8 100644 --- a/src/kv/key.rs +++ b/src/kv/key.rs @@ -30,7 +30,7 @@ impl ToKey for str { } } -/// A key in a user-defined attribute. +/// A key in a key-value. // These impls must only be based on the as_str() representation of the key // If a new field (such as an optional index) is added to the key they must not affect comparison #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] diff --git a/src/kv/mod.rs b/src/kv/mod.rs index deaf4615f..650fc6da2 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -19,62 +19,186 @@ //! data processing techniques, without needing to find and parse attributes from //! unstructured text first. //! -//! In `log`, user-defined attributes are part of a [`Source`] on the [`LogRecord`]. -//! Each attribute is a pair of [`Key`] and [`Value`]. Keys are strings and values -//! are a datum of any type that can be formatted or serialized. Simple types like -//! strings, booleans, and numbers are supported, as well as arbitrarily complex +//! In `log`, user-defined attributes are part of a [`Source`] on the log record. +//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings +//! and values are a datum of any type that can be formatted or serialized. Simple types +//! like strings, booleans, and numbers are supported, as well as arbitrarily complex //! structures involving nested objects and sequences. //! -//! ## Adding attributes to log records +//! ## Adding key-values to log records //! -//! Attributes appear after the message format in the `log!` macros: +//! Key-values appear after the message format in the `log!` macros: //! //! ``` //! .. //! ``` //! -//! ## Working with attributes on log records +//! ## Working with key-values on log records //! -//! Use the [`LogRecord::source`] method to access user-defined attributes. -//! Individual attributes can be pulled from the source: +//! Use the [`LogRecord::key_values`] method to access key-values. +//! +//! Individual values can be pulled from the source by their key: //! //! ``` -//! .. +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{Source, Key, Value}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! // info!("Something of interest"; a = 1); +//! let a: Value = record.key_values().get(Key::from("a")).unwrap(); +//! # Ok(()) +//! # } //! ``` //! -//! This is convenient when an attribute of interest is known in advance. -//! All attributes can also be enumerated using a [`Visitor`]: +//! All key-values can also be enumerated using a [`source::Visitor`]: //! //! ``` -//! .. +//! # fn main() -> Result<(), log::kv::Error> { +//! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); +//! use std::collections::BTreeMap; +//! +//! use log::kv::{self, Source, Key, Value, source::Visitor}; +//! +//! struct Collect<'kvs>(BTreeMap, Value<'kvs>>); +//! +//! impl<'kvs> Visitor<'kvs> for Collect<'kvs> { +//! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { +//! self.0.insert(key, value); +//! +//! Ok(()) +//! } +//! } +//! +//! let mut visitor = Collect(BTreeMap::new()); +//! +//! // info!("Something of interest"; a = 1, b = 2, c = 3); +//! record.key_values().visit(&mut visitor)?; +//! +//! let collected = visitor.0; +//! +//! assert_eq!( +//! vec!["a", "b", "c"], +//! collected +//! .keys() +//! .map(|k| k.as_str()) +//! .collect::>(), +//! ); +//! # Ok(()) +//! # } //! ``` //! -//! [`Value`]s in attributes have methods for conversions to common types: +//! [`Value`]s have methods for conversions to common types: //! //! ``` -//! .. +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{Source, Key}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! // info!("Something of interest"; a = 1); +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!(1, a.to_i64().unwrap()); +//! # Ok(()) +//! # } //! ``` //! -//! Values also have their own [`value::Visitor`] type: +//! Values also have their own [`value::Visitor`] type. Visitors are a lightweight +//! API for working with primitives types: //! //! ``` -//! .. +//! # fn main() -> Result<(), log::kv::Error> { +//! use log::kv::{self, Source, Key, value::Visitor}; +//! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); +//! +//! struct IsNumeric(bool); +//! +//! impl<'kvs> Visitor<'kvs> for IsNumeric { +//! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { +//! self.0 = false; +//! Ok(()) +//! } +//! +//! fn visit_u64(&mut self, _value: u64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_i64(&mut self, _value: i64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_u128(&mut self, _value: u128) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_i128(&mut self, _value: i128) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! +//! fn visit_f64(&mut self, _value: f64) -> Result<(), kv::Error> { +//! self.0 = true; +//! Ok(()) +//! } +//! } +//! +//! // info!("Something of interest"; a = 1); +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! let mut visitor = IsNumeric(false); +//! +//! a.visit(&mut visitor)?; +//! +//! let is_numeric = visitor.0; +//! +//! assert!(is_numeric); +//! # Ok(()) +//! # } //! ``` //! -//! Visitors on values are lightweight and suitable for detecting primitive types. -//! To serialize a value, you can also use either `serde` or `sval`: +//! To serialize a value to a format like JSON, you can also use either `serde` or `sval`: //! //! ``` -//! .. +//! # fn main() -> Result<(), Box> { +//! # #[cfg(feature = "serde")] +//! # { +//! # use log::kv::Key; +//! # #[derive(serde::Serialize)] struct Data { a: i32, b: bool, c: &'static str } +//! let data = Data { a: 1, b: true, c: "Some data" }; +//! # let source = [("a", log::kv::Value::from_serde(&data))]; +//! # let record = log::Record::builder().key_values(&source).build(); +//! +//! // info!("Something of interest"; a = data); +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!("{\"a\":1,\"b\":true,\"c\":\"Some data\"}", serde_json::to_string(&a)?); +//! # } +//! # Ok(()) +//! # } //! ``` //! +//! The choice of serialization framework depends on the needs of the consumer. //! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. +//! Log producers and log consumers don't need to agree on the serialization framework. +//! A value can be captured using its `serde::Serialize` implementation and still be serialized +//! through `sval` without losing any structure. //! //! Values can also always be formatted using the standard `Debug` and `Display` //! traits: //! //! ``` -//! .. +//! # use log::kv::Key; +//! # #[derive(Debug)] struct Data { a: i32, b: bool, c: &'static str } +//! let data = Data { a: 1, b: true, c: "Some data" }; +//! # let source = [("a", log::kv::Value::from_debug(&data))]; +//! # let record = log::Record::builder().key_values(&source).build(); +//! +//! // info!("Something of interest"; a = data); +//! let a = record.key_values().get(Key::from("a")).unwrap(); +//! +//! assert_eq!("Data { a: 1, b: true, c: \"Some data\" }", format!("{a:?}")); //! ``` mod error; diff --git a/src/kv/source.rs b/src/kv/source.rs index 2f62e1999..69544162d 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -1,34 +1,63 @@ -//! Sources for user-defined attributes. +//! Sources for key-values. //! //! This module defines the [`Source`] type and supporting APIs for -//! working with collections of attributes. +//! working with collections of key-values. use crate::kv::{Error, Key, ToKey, ToValue, Value}; use std::fmt; -/// A source of user-defined attributes. +/// A source of key-values. /// /// The source may be a single pair, a set of pairs, or a filter over a set of pairs. /// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data /// in a source. +/// +/// A source is like an iterator over its key-values, except with a push-based API +/// instead of a pull-based one. /// /// # Examples /// -/// Enumerating the attributes in a source: +/// Enumerating the key-values in a source: /// /// ``` -/// .. +/// # fn main() -> Result<(), log::kv::Error> { +/// use log::kv::{self, Source, Key, Value, source::Visitor}; +/// +/// // A `Visitor` that prints all key-values +/// // Visitors are fed the key-value pairs of each key-values +/// struct Printer; +/// +/// impl<'kvs> Visitor<'kvs> for Printer { +/// fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { +/// println!("{key}: {value}"); +/// +/// Ok(()) +/// } +/// } +/// +/// // A source with 3 key-values +/// // Common collection types implement the `Source` trait +/// let source = &[ +/// ("a", 1), +/// ("b", 2), +/// ("c", 3), +/// ]; +/// +/// // Pass an instance of the `Visitor` to a `Source` to visit it +/// source.visit(&mut Printer)?; +/// # Ok(()) +/// # } /// ``` pub trait Source { - /// Visit attributes. + /// Visit key-values. /// - /// A source doesn't have to guarantee any ordering or uniqueness of attributes. + /// A source doesn't have to guarantee any ordering or uniqueness of key-values. /// If the given visitor returns an error then the source may early-return with it, - /// even if there are more attributes. + /// even if there are more key-values. /// /// # Implementation notes /// - /// A source should yield the same attributes to a subsequent visitor unless + /// A source should yield the same key-values to a subsequent visitor unless /// that visitor itself fails. fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; @@ -45,14 +74,14 @@ pub trait Source { get_default(self, key) } - /// Count the number of attributes that can be visited. + /// Count the number of key-values that can be visited. /// /// # Implementation notes /// - /// A source that knows the number of attributes upfront may provide a more + /// A source that knows the number of key-values upfront may provide a more /// efficient implementation. /// - /// A subsequent call to `visit` should yield the same number of attributes + /// A subsequent call to `visit` should yield the same number of key-values /// to the visitor, unless that visitor fails part way through. fn count(&self) -> usize { count_default(self) @@ -165,6 +194,23 @@ where } } +impl Source for [S; N] +where + S: Source, +{ + fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + Source::visit(self as &[_], visitor) + } + + fn get(&self, key: Key) -> Option> { + Source::get(self as &[_], key) + } + + fn count(&self) -> usize { + Source::count(self as &[_]) + } +} + impl Source for Option where S: Source, diff --git a/src/kv/value.rs b/src/kv/value.rs index 67b5427ea..1abc01bde 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -30,7 +30,7 @@ impl<'v> ToValue for Value<'v> { } } -/// A value in a user-defined attribute. +/// A value in a key-value. /// /// Values are an anonymous bag containing some structured datum. /// From 009637717e5b824eb7c1e8d50558b49efd0c6ff0 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 30 Jan 2024 08:02:41 +1000 Subject: [PATCH 07/26] some more kv docs --- src/kv/mod.rs | 81 ++++++++++++++++++---------------- src/kv/source.rs | 14 +++--- src/kv/value.rs | 110 +++++++++++++++++++++++++++++++++++------------ src/lib.rs | 2 +- 4 files changed, 135 insertions(+), 72 deletions(-) diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 650fc6da2..7bff51fb2 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -20,31 +20,33 @@ //! unstructured text first. //! //! In `log`, user-defined attributes are part of a [`Source`] on the log record. -//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings -//! and values are a datum of any type that can be formatted or serialized. Simple types +//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings +//! and values are a datum of any type that can be formatted or serialized. Simple types //! like strings, booleans, and numbers are supported, as well as arbitrarily complex //! structures involving nested objects and sequences. //! //! ## Adding key-values to log records //! -//! Key-values appear after the message format in the `log!` macros: +//! Key-values appear before the message format in the `log!` macros: //! //! ``` -//! .. +//! # use log::info; +//! info!(a = 1; "Something of interest"); //! ``` //! //! ## Working with key-values on log records //! //! Use the [`LogRecord::key_values`] method to access key-values. -//! +//! //! Individual values can be pulled from the source by their key: //! //! ``` //! # fn main() -> Result<(), log::kv::Error> { //! use log::kv::{Source, Key, Value}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); -//! -//! // info!("Something of interest"; a = 1); +//! +//! // info!(a = 1; "Something of interest"); +//! //! let a: Value = record.key_values().get(Key::from("a")).unwrap(); //! # Ok(()) //! # } @@ -56,28 +58,29 @@ //! # fn main() -> Result<(), log::kv::Error> { //! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); //! use std::collections::BTreeMap; -//! +//! //! use log::kv::{self, Source, Key, Value, source::Visitor}; -//! +//! //! struct Collect<'kvs>(BTreeMap, Value<'kvs>>); -//! +//! //! impl<'kvs> Visitor<'kvs> for Collect<'kvs> { //! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { //! self.0.insert(key, value); -//! +//! //! Ok(()) //! } //! } -//! +//! //! let mut visitor = Collect(BTreeMap::new()); -//! -//! // info!("Something of interest"; a = 1, b = 2, c = 3); +//! +//! // info!(a = 1, b = 2, c = 3; "Something of interest"); +//! //! record.key_values().visit(&mut visitor)?; -//! +//! //! let collected = visitor.0; -//! +//! //! assert_eq!( -//! vec!["a", "b", "c"], +//! vec!["a", "b", "c"], //! collected //! .keys() //! .map(|k| k.as_str()) @@ -93,10 +96,11 @@ //! # fn main() -> Result<(), log::kv::Error> { //! use log::kv::{Source, Key}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); -//! -//! // info!("Something of interest"; a = 1); +//! +//! // info!(a = 1; "Something of interest"); +//! //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! assert_eq!(1, a.to_i64().unwrap()); //! # Ok(()) //! # } @@ -109,9 +113,9 @@ //! # fn main() -> Result<(), log::kv::Error> { //! use log::kv::{self, Source, Key, value::Visitor}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); -//! +//! //! struct IsNumeric(bool); -//! +//! //! impl<'kvs> Visitor<'kvs> for IsNumeric { //! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { //! self.0 = false; @@ -143,23 +147,24 @@ //! Ok(()) //! } //! } -//! -//! // info!("Something of interest"; a = 1); +//! +//! // info!(a = 1; "Something of interest"); +//! //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! let mut visitor = IsNumeric(false); -//! +//! //! a.visit(&mut visitor)?; -//! +//! //! let is_numeric = visitor.0; -//! +//! //! assert!(is_numeric); //! # Ok(()) //! # } //! ``` //! //! To serialize a value to a format like JSON, you can also use either `serde` or `sval`: -//! +//! //! ``` //! # fn main() -> Result<(), Box> { //! # #[cfg(feature = "serde")] @@ -169,16 +174,17 @@ //! let data = Data { a: 1, b: true, c: "Some data" }; //! # let source = [("a", log::kv::Value::from_serde(&data))]; //! # let record = log::Record::builder().key_values(&source).build(); -//! -//! // info!("Something of interest"; a = data); +//! +//! // info!(a = data; "Something of interest"); +//! //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! assert_eq!("{\"a\":1,\"b\":true,\"c\":\"Some data\"}", serde_json::to_string(&a)?); //! # } //! # Ok(()) //! # } //! ``` -//! +//! //! The choice of serialization framework depends on the needs of the consumer. //! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. //! Log producers and log consumers don't need to agree on the serialization framework. @@ -187,17 +193,18 @@ //! //! Values can also always be formatted using the standard `Debug` and `Display` //! traits: -//! +//! //! ``` //! # use log::kv::Key; //! # #[derive(Debug)] struct Data { a: i32, b: bool, c: &'static str } //! let data = Data { a: 1, b: true, c: "Some data" }; //! # let source = [("a", log::kv::Value::from_debug(&data))]; //! # let record = log::Record::builder().key_values(&source).build(); -//! -//! // info!("Something of interest"; a = data); +//! +//! // info!(a = data; "Something of interest"); +//! //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! assert_eq!("Data { a: 1, b: true, c: \"Some data\" }", format!("{a:?}")); //! ``` diff --git a/src/kv/source.rs b/src/kv/source.rs index 69544162d..87d955417 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -1,5 +1,5 @@ //! Sources for key-values. -//! +//! //! This module defines the [`Source`] type and supporting APIs for //! working with collections of key-values. @@ -11,7 +11,7 @@ use std::fmt; /// The source may be a single pair, a set of pairs, or a filter over a set of pairs. /// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data /// in a source. -/// +/// /// A source is like an iterator over its key-values, except with a push-based API /// instead of a pull-based one. /// @@ -22,19 +22,19 @@ use std::fmt; /// ``` /// # fn main() -> Result<(), log::kv::Error> { /// use log::kv::{self, Source, Key, Value, source::Visitor}; -/// +/// /// // A `Visitor` that prints all key-values /// // Visitors are fed the key-value pairs of each key-values /// struct Printer; -/// +/// /// impl<'kvs> Visitor<'kvs> for Printer { /// fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { /// println!("{key}: {value}"); -/// +/// /// Ok(()) /// } /// } -/// +/// /// // A source with 3 key-values /// // Common collection types implement the `Source` trait /// let source = &[ @@ -42,7 +42,7 @@ use std::fmt; /// ("b", 2), /// ("c", 3), /// ]; -/// +/// /// // Pass an instance of the `Visitor` to a `Source` to visit it /// source.visit(&mut Printer)?; /// # Ok(()) diff --git a/src/kv/value.rs b/src/kv/value.rs index 1abc01bde..c8cdf6dbb 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -1,5 +1,5 @@ //! Structured values. -//! +//! //! This module defines the [`Value`] type and supporting APIs for //! capturing and serializing them. @@ -45,7 +45,6 @@ impl<'v> ToValue for Value<'v> { /// ## Using the `Value::from_*` methods /// /// `Value` offers a few constructor methods that capture values of different kinds. -/// These methods don't require `T: 'static`, but can't support downcasting. /// /// ``` /// use log::kv::Value; @@ -79,25 +78,43 @@ impl<'v> ToValue for Value<'v> { /// assert_eq!(Some(42), value.to_i64()); /// ``` /// +/// # Data model +/// +/// Values can hold one of a number of types: +/// +/// - **Null:** The absence of any other meaningful value. Note that +/// `Some(Value::null())` is not the same as `None`. The former is +/// `null` while the latter is `undefined`. This is important to be +/// able to tell the difference between a key-value that was logged, +/// but its value was empty (`Some(Value::null())`) and a key-value +/// that was never logged at all (`None`). +/// - **Strings:** `str`, `char`. +/// - **Booleans:** `bool`. +/// - **Integers:** `u8`-`u128`, `i8`-`i128`, `NonZero*`. +/// - **Floating point numbers:** `f32`-`f64`. +/// - **Errors:** `dyn (Error + 'static)`. +/// - **`serde`:** Any type in `serde`'s data model. +/// - **`sval`:** Any type in `sval`'s data model. +/// /// # Serialization /// -/// `Value` provides a number of ways to be serialized. +/// Values provide a number of ways to be serialized. /// /// For basic types the [`Value::visit`] method can be used to extract the /// underlying typed value. However this is limited in the amount of types -/// supported (see the [`Visit`] trait methods). +/// supported (see the [`Visitor`] trait methods). /// /// For more complex types one of the following traits can be used: -/// * [`sval::Value`], requires the `kv_unstable_sval` feature. -/// * [`serde::Serialize`], requires the `kv_unstable_serde` feature. -/// -/// You don't need a [`Visit`] to serialize values. -/// +/// * `sval::Value`, requires the `kv_unstable_sval` feature. +/// * `serde::Serialize`, requires the `kv_unstable_serde` feature. +/// +/// You don't need a visitor to serialize values through `serde` or `sval`. +/// /// A value can always be serialized using any supported framework, regardless /// of how it was captured. If, for example, a value was captured using its -/// `Display` implementation, it will serialize as a string. If it was captured -/// as a struct using its `serde::Serialize`, it will also serialize as a struct -/// through `sval`, or be formatted using a `Debug`-compatible representation. +/// `Display` implementation, it will serialize through `serde` as a string. If it was +/// captured as a struct using `serde`, it will also serialize as a struct +/// through `sval`, or can be formatted using a `Debug`-compatible representation. pub struct Value<'v> { inner: inner::Inner<'v>, } @@ -175,6 +192,13 @@ impl<'v> Value<'v> { } } + /// Get a `null` value. + pub fn null() -> Self { + Value { + inner: inner::Inner::empty(), + } + } + /// Get a value from an internal primitive. fn from_inner(value: T) -> Self where @@ -458,15 +482,22 @@ mod std_support { } } -/// A visitor for a `Value`. +/// A visitor for a [`Value`]. /// -/// Also see [`Value`'s documentation on seralization]. +/// Also see [`Value`'s documentation on seralization]. Visitors are a simple alternative +/// to a more fully-featured serialization framework like `serde` or `sval`. A visitor +/// can differentiate primitive types through methods like [`Visitor::visit_bool`] and +/// [`Visitor::visit_str`], but more complex types like maps and sequences +/// will fallthrough to [`Visitor::visit_any`]. +/// +/// If you're trying to serialize a value to a format like JSON, you can use either `serde` +/// or `sval` directly with the value. You don't need a visitor. /// /// [`Value`'s documentation on seralization]: Value#serialization pub trait Visitor<'v> { /// Visit a `Value`. /// - /// This is the only required method on `Visit` and acts as a fallback for any + /// This is the only required method on `Visitor` 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. @@ -622,39 +653,57 @@ pub(in crate::kv) mod inner { } fn visit_u64(&mut self, value: u64) -> Result<(), Error> { - self.0.visit_u64(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_u64(value) + .map_err(crate::kv::Error::into_value) } fn visit_i64(&mut self, value: i64) -> Result<(), Error> { - self.0.visit_i64(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_i64(value) + .map_err(crate::kv::Error::into_value) } fn visit_u128(&mut self, value: u128) -> Result<(), Error> { - self.0.visit_u128(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_u128(value) + .map_err(crate::kv::Error::into_value) } fn visit_i128(&mut self, value: i128) -> Result<(), Error> { - self.0.visit_i128(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_i128(value) + .map_err(crate::kv::Error::into_value) } fn visit_f64(&mut self, value: f64) -> Result<(), Error> { - self.0.visit_f64(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_f64(value) + .map_err(crate::kv::Error::into_value) } fn visit_bool(&mut self, value: bool) -> Result<(), Error> { - self.0.visit_bool(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_bool(value) + .map_err(crate::kv::Error::into_value) } fn visit_str(&mut self, value: &str) -> Result<(), Error> { - self.0.visit_str(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_str(value) + .map_err(crate::kv::Error::into_value) } fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { - self.0.visit_borrowed_str(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_borrowed_str(value) + .map_err(crate::kv::Error::into_value) } fn visit_char(&mut self, value: char) -> Result<(), Error> { - self.0.visit_char(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_char(value) + .map_err(crate::kv::Error::into_value) } #[cfg(feature = "kv_unstable_std")] @@ -662,7 +711,9 @@ pub(in crate::kv) mod inner { &mut self, err: &(dyn std::error::Error + 'static), ) -> Result<(), Error> { - self.0.visit_error(err).map_err(crate::kv::Error::into_value) + self.0 + .visit_error(err) + .map_err(crate::kv::Error::into_value) } #[cfg(feature = "kv_unstable_std")] @@ -670,7 +721,9 @@ pub(in crate::kv) mod inner { &mut self, err: &'v (dyn std::error::Error + 'static), ) -> Result<(), Error> { - self.0.visit_borrowed_error(err).map_err(crate::kv::Error::into_value) + self.0 + .visit_borrowed_error(err) + .map_err(crate::kv::Error::into_value) } } @@ -826,6 +879,10 @@ pub(in crate::kv) mod inner { todo!() } + pub fn empty() -> Self { + todo!() + } + pub fn to_bool(&self) -> Option { todo!() } @@ -873,7 +930,6 @@ pub(in crate::kv) mod inner { F64(f64), I64(i64), U64(u64), - } #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index 6a8e57762..86f3f8ec2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,7 +121,7 @@ //! # #[cfg(not(feature = "kv_unstable_serde"))] //! # fn main() {} //! ``` -//! +//! //! See the [`kv`] module documentation for more details. //! //! # Available logging implementations From 54c34f7fbd330c3dffd10addd75480e527a0bae5 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 30 Jan 2024 20:35:35 +1000 Subject: [PATCH 08/26] start filling in no-dependency value --- Cargo.toml | 2 +- src/kv/value.rs | 244 ++++++++++++++++++++++++++++++------------------ 2 files changed, 152 insertions(+), 94 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ef0e3eb7c..690f25cee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"] serde = { version = "1.0", optional = true, default-features = false } sval = { version = "2.1", optional = true, default-features = false } sval_ref = { version = "2.1", optional = true, default-features = false } -value-bag = { version = "1.4", optional = true, default-features = false } +value-bag = { version = "1.4", optional = true, default-features = false, features = ["inline-i128"] } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } diff --git a/src/kv/value.rs b/src/kv/value.rs index c8cdf6dbb..f5f4fc057 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -260,62 +260,12 @@ impl ToValue for str { } } -impl ToValue for u128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for i128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for std::num::NonZeroU128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - -impl ToValue for std::num::NonZeroI128 { - fn to_value(&self) -> Value { - Value::from(self) - } -} - impl<'v> From<&'v str> for Value<'v> { fn from(value: &'v str) -> Self { Value::from_inner(value) } } -impl<'v> From<&'v u128> for Value<'v> { - fn from(value: &'v u128) -> Self { - Value::from_inner(value) - } -} - -impl<'v> From<&'v i128> for Value<'v> { - fn from(value: &'v i128) -> Self { - Value::from_inner(value) - } -} - -impl<'v> From<&'v std::num::NonZeroU128> for Value<'v> { - fn from(v: &'v std::num::NonZeroU128) -> Value<'v> { - // SAFETY: `NonZeroU128` and `u128` have the same ABI - Value::from_inner(unsafe { &*(v as *const std::num::NonZeroU128 as *const u128) }) - } -} - -impl<'v> From<&'v std::num::NonZeroI128> for Value<'v> { - fn from(v: &'v std::num::NonZeroI128) -> Value<'v> { - // SAFETY: `NonZeroI128` and `i128` have the same ABI - Value::from_inner(unsafe { &*(v as *const std::num::NonZeroI128 as *const i128) }) - } -} - impl ToValue for () { fn to_value(&self) -> Value { Value::from_inner(()) @@ -383,12 +333,12 @@ macro_rules! impl_value_to_primitive { } } -impl_to_value_primitive![usize, u8, u16, u32, u64, isize, i8, i16, i32, i64, f32, f64, char, bool,]; +impl_to_value_primitive![usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool,]; #[rustfmt::skip] impl_to_value_nonzero_primitive![ - NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, - NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, + NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, + NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128, ]; impl_value_to_primitive![ @@ -503,6 +453,11 @@ pub trait Visitor<'v> { /// or serialized using its `sval::Value` or `serde::Serialize` implementation. fn visit_any(&mut self, value: Value) -> Result<(), Error>; + /// Visit an empty value. + fn visit_null(&mut self) -> Result<(), Error> { + self.visit_any(Value::null()) + } + /// Visit an unsigned integer. fn visit_u64(&mut self, value: u64) -> Result<(), Error> { self.visit_any(value.into()) @@ -515,12 +470,12 @@ pub trait Visitor<'v> { /// Visit a big unsigned integer. fn visit_u128(&mut self, value: u128) -> Result<(), Error> { - self.visit_any((&value).into()) + self.visit_any((value).into()) } /// Visit a big signed integer. fn visit_i128(&mut self, value: i128) -> Result<(), Error> { - self.visit_any((&value).into()) + self.visit_any((value).into()) } /// Visit a floating point. @@ -573,6 +528,10 @@ where (**self).visit_any(value) } + fn visit_null(&mut self) -> Result<(), Error> { + (**self).visit_null() + } + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { (**self).visit_u64(value) } @@ -739,188 +698,287 @@ pub(in crate::kv) mod inner { #[derive(Clone)] pub enum Inner<'v> { + None, + Bool(bool), Str(&'v str), + Char(char), + I64(i64), + U64(u64), + F64(f64), + I128(i128), + U128(u128), + Debug(&'v dyn fmt::Debug), + Display(&'v dyn fmt::Display), } impl<'v> From<()> for Inner<'v> { fn from(v: ()) -> Self { - todo!() + Inner::None } } impl<'v> From for Inner<'v> { fn from(v: bool) -> Self { - todo!() + Inner::Bool(v) } } impl<'v> From for Inner<'v> { fn from(v: char) -> Self { - todo!() + Inner::Char(v) } } impl<'v> From for Inner<'v> { fn from(v: f32) -> Self { - todo!() + Inner::F64(v as f64) } } impl<'v> From for Inner<'v> { fn from(v: f64) -> Self { - todo!() + Inner::F64(v) } } impl<'v> From for Inner<'v> { fn from(v: i8) -> Self { - todo!() + Inner::I64(v as i64) } } impl<'v> From for Inner<'v> { fn from(v: i16) -> Self { - todo!() + Inner::I64(v as i64) } } impl<'v> From for Inner<'v> { fn from(v: i32) -> Self { - todo!() + Inner::I64(v as i64) } } impl<'v> From for Inner<'v> { fn from(v: i64) -> Self { - todo!() + Inner::I64(v as i64) } } impl<'v> From for Inner<'v> { fn from(v: isize) -> Self { - todo!() + Inner::I64(v as i64) } } impl<'v> From for Inner<'v> { fn from(v: u8) -> Self { - todo!() + Inner::U64(v as u64) } } impl<'v> From for Inner<'v> { fn from(v: u16) -> Self { - todo!() + Inner::U64(v as u64) } } impl<'v> From for Inner<'v> { fn from(v: u32) -> Self { - todo!() + Inner::U64(v as u64) } } impl<'v> From for Inner<'v> { fn from(v: u64) -> Self { - todo!() + Inner::U64(v as u64) } } impl<'v> From for Inner<'v> { fn from(v: usize) -> Self { - todo!() + Inner::U64(v as u64) } } - impl<'v> From<&'v i128> for Inner<'v> { - fn from(v: &'v i128) -> Self { - todo!() + impl<'v> From for Inner<'v> { + fn from(v: i128) -> Self { + Inner::I128(v) } } - impl<'v> From<&'v u128> for Inner<'v> { - fn from(v: &'v u128) -> Self { - todo!() + impl<'v> From for Inner<'v> { + fn from(v: u128) -> Self { + Inner::U128(v) } } impl<'v> From<&'v str> for Inner<'v> { fn from(v: &'v str) -> Self { - todo!() + Inner::Str(v) } } impl<'v> fmt::Debug for Inner<'v> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - todo!() + match self { + Inner::None => fmt::Debug::fmt(&None::<()>, f), + Inner::Bool(v) => fmt::Debug::fmt(v, f), + Inner::Str(v) => fmt::Debug::fmt(v, f), + Inner::Char(v) => fmt::Debug::fmt(v, f), + Inner::I64(v) => fmt::Debug::fmt(v, f), + Inner::U64(v) => fmt::Debug::fmt(v, f), + Inner::F64(v) => fmt::Debug::fmt(v, f), + Inner::I128(v) => fmt::Debug::fmt(v, f), + Inner::U128(v) => fmt::Debug::fmt(v, f), + Inner::Debug(v) => fmt::Debug::fmt(v, f), + Inner::Display(v) => fmt::Display::fmt(v, f), + } } } impl<'v> fmt::Display for Inner<'v> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - todo!() + match self { + Inner::None => fmt::Debug::fmt(&None::<()>, f), + Inner::Bool(v) => fmt::Display::fmt(v, f), + Inner::Str(v) => fmt::Display::fmt(v, f), + Inner::Char(v) => fmt::Display::fmt(v, f), + Inner::I64(v) => fmt::Display::fmt(v, f), + Inner::U64(v) => fmt::Display::fmt(v, f), + Inner::F64(v) => fmt::Display::fmt(v, f), + Inner::I128(v) => fmt::Display::fmt(v, f), + Inner::U128(v) => fmt::Display::fmt(v, f), + Inner::Debug(v) => fmt::Debug::fmt(v, f), + Inner::Display(v) => fmt::Display::fmt(v, f), + } } } impl<'v> Inner<'v> { pub fn from_debug(value: &'v T) -> Self { - todo!() + Inner::Debug(value) } pub fn from_display(value: &'v T) -> Self { - todo!() + Inner::Display(value) } pub fn from_dyn_debug(value: &'v dyn fmt::Debug) -> Self { - todo!() + Inner::Debug(value) } pub fn from_dyn_display(value: &'v dyn fmt::Display) -> Self { - todo!() + Inner::Display(value) } pub fn empty() -> Self { - todo!() + Inner::None } pub fn to_bool(&self) -> Option { - todo!() + match self { + Inner::Bool(v) => Some(*v), + _ => None, + } } pub fn to_char(&self) -> Option { - todo!() + match self { + Inner::Char(v) => Some(*v), + _ => None, + } } pub fn to_f64(&self) -> Option { - todo!() + match self { + Inner::F64(v) => Some(*v), + Inner::I64(v) => { + let v: i32 = (*v).try_into().ok()?; + v.try_into().ok() + }, + Inner::U64(v) => { + let v: u32 = (*v).try_into().ok()?; + v.try_into().ok() + }, + Inner::I128(v) => { + let v: i32 = (*v).try_into().ok()?; + v.try_into().ok() + }, + Inner::U128(v) => { + let v: u32 = (*v).try_into().ok()?; + v.try_into().ok() + }, + _ => None, + } } pub fn to_i64(&self) -> Option { - todo!() + match self { + Inner::I64(v) => Some(*v), + Inner::U64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } } pub fn to_u64(&self) -> Option { - todo!() + match self { + Inner::U64(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } } pub fn to_u128(&self) -> Option { - todo!() + match self { + Inner::U128(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::U64(v) => (*v).try_into().ok(), + Inner::I128(v) => (*v).try_into().ok(), + _ => None, + } } pub fn to_i128(&self) -> Option { - todo!() + match self { + Inner::I128(v) => Some(*v), + Inner::I64(v) => (*v).try_into().ok(), + Inner::U64(v) => (*v).try_into().ok(), + Inner::U128(v) => (*v).try_into().ok(), + _ => None, + } } pub fn to_borrowed_str(&self) -> Option<&'v str> { - todo!() + match self { + Inner::Str(v) => Some(v), + _ => None, + } } #[cfg(test)] pub fn to_test_token(&self) -> Token { - todo!() + match self { + Inner::None => Token::None, + Inner::Bool(v) => Token::Bool(*v), + Inner::Str(v) => Token::Str(*v), + Inner::Char(v) => Token::Char(*v), + Inner::I64(v) => Token::I64(*v), + Inner::U64(v) => Token::U64(*v), + Inner::F64(v) => Token::F64(*v), + Inner::I128(v) => unimplemented!(), + Inner::U128(v) => unimplemented!(), + Inner::Debug(v) => unimplemented!(), + Inner::Display(v) => unimplemented!(), + } } } + #[cfg(test)] #[derive(Debug, PartialEq)] pub enum Token<'v> { None, From 05d7bed9e405d83e417d1748e03888976974ef4c Mon Sep 17 00:00:00 2001 From: KodrAus Date: Wed, 31 Jan 2024 10:42:32 +1000 Subject: [PATCH 09/26] fill in visitor for inline value --- Cargo.toml | 4 ++-- src/kv/value.rs | 20 ++++++++++++++++++-- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 690f25cee..c30944c56 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"] serde = { version = "1.0", optional = true, default-features = false } sval = { version = "2.1", optional = true, default-features = false } sval_ref = { version = "2.1", optional = true, default-features = false } -value-bag = { version = "1.4", optional = true, default-features = false, features = ["inline-i128"] } +value-bag = { version = "1.7", optional = true, default-features = false, features = ["inline-i128"] } [dev-dependencies] serde = { version = "1.0", features = ["derive"] } @@ -65,7 +65,7 @@ serde_json = "1.0" serde_test = "1.0" sval = { version = "2.1" } sval_derive = { version = "2.1" } -value-bag = { version = "1.4", features = ["test"] } +value-bag = { version = "1.7", features = ["test"] } # NOTE: log doesn't actually depent on this crate. However our dependencies, # serde and sval, dependent on version 1.0 of the crate, which has problem fixed diff --git a/src/kv/value.rs b/src/kv/value.rs index f5f4fc057..2a16f192c 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -611,6 +611,10 @@ pub(in crate::kv) mod inner { .map_err(crate::kv::Error::into_value) } + fn visit_empty(&mut self) -> Result<(), Error> { + self.0.visit_null().map_err(crate::kv::Error::into_value) + } + fn visit_u64(&mut self, value: u64) -> Result<(), Error> { self.0 .visit_u64(value) @@ -1005,8 +1009,20 @@ pub(in crate::kv) mod inner { } } - pub fn visit<'v>(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { - todo!() + pub fn visit<'v>(inner: &Inner<'v>, mut visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { + match inner { + Inner::None => visitor.visit_null(), + Inner::Bool(v) => visitor.visit_bool(*v), + Inner::Str(v) => visitor.visit_borrowed_str(*v), + Inner::Char(v) => visitor.visit_char(*v), + Inner::I64(v) => visitor.visit_i64(*v), + Inner::U64(v) => visitor.visit_u64(*v), + Inner::F64(v) => visitor.visit_f64(*v), + Inner::I128(v) => visitor.visit_i128(*v), + Inner::U128(v) => visitor.visit_u128(*v), + Inner::Debug(v) => visitor.visit_any(Value::from_dyn_debug(*v)), + Inner::Display(v) => visitor.visit_any(Value::from_dyn_display(*v)), + } } } From e711b62c7efd2bb1a2cdb48160bf65b9ae0f3355 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Wed, 31 Jan 2024 12:28:02 +1000 Subject: [PATCH 10/26] fix up some warnings --- src/kv/error.rs | 20 +++++++++++--------- src/kv/value.rs | 17 +---------------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/src/kv/error.rs b/src/kv/error.rs index a6f48d0c6..d654be780 100644 --- a/src/kv/error.rs +++ b/src/kv/error.rs @@ -1,7 +1,5 @@ use std::fmt; -use crate::kv::value; - /// An error encountered while working with structured data. #[derive(Debug)] pub struct Error { @@ -13,7 +11,8 @@ enum Inner { #[cfg(feature = "std")] Boxed(std_support::BoxedError), Msg(&'static str), - Value(value::inner::Error), + #[cfg(feature = "value-bag")] + Value(crate::kv::value::inner::Error), Fmt, } @@ -25,21 +24,23 @@ impl Error { } } - // Not public so we don't leak the `value::inner` API - pub(super) fn from_value(err: value::inner::Error) -> Self { + // Not public so we don't leak the `crate::kv::value::inner` API + #[cfg(feature = "value-bag")] + pub(super) fn from_value(err: crate::kv::value::inner::Error) -> Self { Error { inner: Inner::Value(err), } } - // Not public so we don't leak the `value::inner` API - pub(super) fn into_value(self) -> value::inner::Error { + // Not public so we don't leak the `crate::kv::value::inner` API + #[cfg(feature = "value-bag")] + pub(super) fn into_value(self) -> crate::kv::value::inner::Error { match self.inner { Inner::Value(err) => err, #[cfg(feature = "kv_unstable_std")] - _ => value::inner::Error::boxed(self), + _ => crate::kv::value::inner::Error::boxed(self), #[cfg(not(feature = "kv_unstable_std"))] - _ => value::inner::Error::msg("error inspecting a value"), + _ => crate::kv::value::inner::Error::msg("error inspecting a value"), } } } @@ -50,6 +51,7 @@ impl fmt::Display for Error { match &self.inner { #[cfg(feature = "std")] Boxed(err) => err.fmt(f), + #[cfg(feature = "value-bag")] Value(err) => err.fmt(f), Msg(msg) => msg.fmt(f), Fmt => fmt::Error.fmt(f), diff --git a/src/kv/value.rs b/src/kv/value.rs index 2a16f192c..60828a27a 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -716,7 +716,7 @@ pub(in crate::kv) mod inner { } impl<'v> From<()> for Inner<'v> { - fn from(v: ()) -> Self { + fn from(_: ()) -> Self { Inner::None } } @@ -994,21 +994,6 @@ pub(in crate::kv) mod inner { U64(u64), } - #[derive(Debug)] - pub struct Error {} - - impl Error { - pub fn msg(msg: &'static str) -> Self { - todo!() - } - } - - impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - todo!() - } - } - pub fn visit<'v>(inner: &Inner<'v>, mut visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { match inner { Inner::None => visitor.visit_null(), From 67885e05f630243938667319f684d890f146703e Mon Sep 17 00:00:00 2001 From: KodrAus Date: Wed, 31 Jan 2024 13:07:59 +1000 Subject: [PATCH 11/26] add some docs on how Value is implemented --- src/kv/value.rs | 55 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/kv/value.rs b/src/kv/value.rs index 60828a27a..971bf6bbf 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -333,7 +333,9 @@ macro_rules! impl_value_to_primitive { } } -impl_to_value_primitive![usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool,]; +impl_to_value_primitive![ + usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool, +]; #[rustfmt::skip] impl_to_value_nonzero_primitive![ @@ -584,6 +586,13 @@ where #[cfg(feature = "value-bag")] pub(in crate::kv) mod inner { + /** + An implementation of `Value` based on a library called `value_bag`. + + `value_bag` was written specifically for use in `log`'s value, but was split out when it outgrew + the codebase here. It's a general-purpose type-erasure library that handles mapping between + more fully-featured serialization frameworks. + */ use super::*; pub use value_bag::ValueBag as Inner; @@ -698,6 +707,19 @@ pub(in crate::kv) mod inner { #[cfg(not(feature = "value-bag"))] pub(in crate::kv) mod inner { + /** + This is a dependency-free implementation of `Value` when there's no serialization frameworks involved. + In these simple cases a more fully featured solution like `value_bag` isn't needed, so we avoid pulling it in. + + There are a few things here that need to remain consistent with the `value_bag`-based implementation: + + 1. Conversions should always produce the same results. If a conversion here returns `Some`, then + the same `value_bag`-based conversion must also. Of particular note here are floats to ints; they're + based on the standard library's `TryInto` conversions, which need to be convert to `i32` or `u32`, + and then to `f64`. + 2. Visitors should always be called in the same way. If a particular type of value calls `visit_i64`, + then the same `value_bag`-based visitor must also. + */ use super::*; #[derive(Clone)] @@ -900,19 +922,19 @@ pub(in crate::kv) mod inner { Inner::I64(v) => { let v: i32 = (*v).try_into().ok()?; v.try_into().ok() - }, + } Inner::U64(v) => { let v: u32 = (*v).try_into().ok()?; v.try_into().ok() - }, + } Inner::I128(v) => { let v: i32 = (*v).try_into().ok()?; v.try_into().ok() - }, + } Inner::U128(v) => { let v: u32 = (*v).try_into().ok()?; v.try_into().ok() - }, + } _ => None, } } @@ -974,10 +996,10 @@ pub(in crate::kv) mod inner { Inner::I64(v) => Token::I64(*v), Inner::U64(v) => Token::U64(*v), Inner::F64(v) => Token::F64(*v), - Inner::I128(v) => unimplemented!(), - Inner::U128(v) => unimplemented!(), - Inner::Debug(v) => unimplemented!(), - Inner::Display(v) => unimplemented!(), + Inner::I128(_) => unimplemented!(), + Inner::U128(_) => unimplemented!(), + Inner::Debug(_) => unimplemented!(), + Inner::Display(_) => unimplemented!(), } } } @@ -994,7 +1016,10 @@ pub(in crate::kv) mod inner { U64(u64), } - pub fn visit<'v>(inner: &Inner<'v>, mut visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { + pub fn visit<'v>( + inner: &Inner<'v>, + mut visitor: impl Visitor<'v>, + ) -> Result<(), crate::kv::Error> { match inner { Inner::None => visitor.visit_null(), Inner::Bool(v) => visitor.visit_bool(*v), @@ -1120,6 +1145,16 @@ pub(crate) mod tests { } } + #[test] + fn test_to_float() { + // Only integers from i32::MIN..=u32::MAX can be converted into floats + assert!(Value::from(i32::MIN).to_f64().is_some()); + assert!(Value::from(u32::MAX).to_f64().is_some()); + + assert!(Value::from((i32::MIN as i64) - 1).to_f64().is_none()); + assert!(Value::from((u32::MAX as u64) + 1).to_f64().is_none()); + } + #[test] fn test_to_cow_str() { for v in str() { From 0374a25554b9f2af5a4f442ea63325f20115d81e Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 13 Feb 2024 20:07:58 +1000 Subject: [PATCH 12/26] work on macro support for capturing --- README.md | 6 +-- src/__private_api.rs | 54 ++++++++++++++++---- src/kv/mod.rs | 58 ++++++++++----------- src/kv/source.rs | 14 ++--- src/kv/value.rs | 51 +++++++++++++------ src/lib.rs | 8 +-- src/macros.rs | 119 ++++++++++++++++++++++++++++++++++++++++++- tests/Cargo.toml | 4 ++ tests/macros.rs | 47 +++++++++++++++++ 9 files changed, 292 insertions(+), 69 deletions(-) diff --git a/README.md b/README.md index d41414473..7b188719f 100644 --- a/README.md +++ b/README.md @@ -103,10 +103,10 @@ The executable itself may use the `log` crate to log as well. If you enable the `kv_unstable` feature, you can associate structured data with your log records: ```rust -use log::{info, trace, warn, as_serde, as_error}; +use log::{info, trace, warn}; pub fn shave_the_yak(yak: &mut Yak) { - trace!(target = "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); + trace!(target = "yak_events", yak:serde = yak; "Commencing yak shaving"); loop { match find_a_razor() { @@ -116,7 +116,7 @@ pub fn shave_the_yak(yak: &mut Yak) { break; } Err(err) => { - warn!(err = as_error!(err); "Unable to locate a razor, retrying"); + warn!(err:error = err; "Unable to locate a razor, retrying"); } } } diff --git a/src/__private_api.rs b/src/__private_api.rs index 92bd15656..8ea869958 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -5,31 +5,28 @@ use crate::{Level, Metadata, Record}; use std::fmt::Arguments; pub use std::{file, format_args, line, module_path, stringify}; -#[cfg(feature = "kv_unstable")] -pub type Value<'a> = dyn crate::kv::value::ToValue + 'a; - #[cfg(not(feature = "kv_unstable"))] -pub type Value<'a> = str; +pub type Value<'a> = &'a str; mod sealed { /// Types for the `kv` argument. pub trait KVs<'a> { - fn into_kvs(self) -> Option<&'a [(&'a str, &'a super::Value<'a>)]>; + fn into_kvs(self) -> Option<&'a [(&'a str, super::Value<'a>)]>; } } // Types for the `kv` argument. -impl<'a> KVs<'a> for &'a [(&'a str, &'a Value<'a>)] { +impl<'a> KVs<'a> for &'a [(&'a str, Value<'a>)] { #[inline] - fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { + fn into_kvs(self) -> Option<&'a [(&'a str, Value<'a>)]> { Some(self) } } impl<'a> KVs<'a> for () { #[inline] - fn into_kvs(self) -> Option<&'a [(&'a str, &'a Value<'a>)]> { + fn into_kvs(self) -> Option<&'a [(&'a str, Value<'a>)]> { None } } @@ -41,7 +38,7 @@ fn log_impl( level: Level, &(target, module_path, file): &(&str, &'static str, &'static str), line: u32, - kvs: Option<&[(&str, &Value)]>, + kvs: Option<&[(&str, Value)]>, ) { #[cfg(not(feature = "kv_unstable"))] if kvs.is_some() { @@ -87,3 +84,42 @@ pub fn log<'a, K>( pub fn enabled(level: Level, target: &str) -> bool { crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) } + +#[cfg(feature = "kv_unstable")] +mod kv_support { + use super::*; + + use crate::kv; + + pub type Value<'a> = kv::Value<'a>; + + pub fn capture_to_value<'a, V: kv::value::ToValue + ?Sized>(v: &'a &'a V) -> Value<'a> { + v.to_value() + } + + pub fn capture_debug<'a, V: core::fmt::Debug + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_debug(v) + } + + pub fn capture_display<'a, V: core::fmt::Display + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_display(v) + } + + #[cfg(feature = "kv_unstable_std")] + pub fn capture_error<'a>(v: &'a (dyn std::error::Error + 'static)) -> Value<'a> { + Value::from_dyn_error(v) + } + + #[cfg(feature = "kv_unstable_sval")] + pub fn capture_sval<'a, V: sval::Value + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_sval(v) + } + + #[cfg(feature = "kv_unstable_serde")] + pub fn capture_serde<'a, V: serde::Serialize + ?Sized>(v: &'a &'a V) -> Value<'a> { + Value::from_serde(v) + } +} + +#[cfg(feature = "kv_unstable")] +pub use self::kv_support::*; diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 650fc6da2..1b86bda93 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -20,8 +20,8 @@ //! unstructured text first. //! //! In `log`, user-defined attributes are part of a [`Source`] on the log record. -//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings -//! and values are a datum of any type that can be formatted or serialized. Simple types +//! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings +//! and values are a datum of any type that can be formatted or serialized. Simple types //! like strings, booleans, and numbers are supported, as well as arbitrarily complex //! structures involving nested objects and sequences. //! @@ -36,14 +36,14 @@ //! ## Working with key-values on log records //! //! Use the [`LogRecord::key_values`] method to access key-values. -//! +//! //! Individual values can be pulled from the source by their key: //! //! ``` //! # fn main() -> Result<(), log::kv::Error> { //! use log::kv::{Source, Key, Value}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); -//! +//! //! // info!("Something of interest"; a = 1); //! let a: Value = record.key_values().get(Key::from("a")).unwrap(); //! # Ok(()) @@ -56,28 +56,28 @@ //! # fn main() -> Result<(), log::kv::Error> { //! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); //! use std::collections::BTreeMap; -//! +//! //! use log::kv::{self, Source, Key, Value, source::Visitor}; -//! +//! //! struct Collect<'kvs>(BTreeMap, Value<'kvs>>); -//! +//! //! impl<'kvs> Visitor<'kvs> for Collect<'kvs> { //! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { //! self.0.insert(key, value); -//! +//! //! Ok(()) //! } //! } -//! +//! //! let mut visitor = Collect(BTreeMap::new()); -//! +//! //! // info!("Something of interest"; a = 1, b = 2, c = 3); //! record.key_values().visit(&mut visitor)?; -//! +//! //! let collected = visitor.0; -//! +//! //! assert_eq!( -//! vec!["a", "b", "c"], +//! vec!["a", "b", "c"], //! collected //! .keys() //! .map(|k| k.as_str()) @@ -93,10 +93,10 @@ //! # fn main() -> Result<(), log::kv::Error> { //! use log::kv::{Source, Key}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); -//! +//! //! // info!("Something of interest"; a = 1); //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! assert_eq!(1, a.to_i64().unwrap()); //! # Ok(()) //! # } @@ -109,9 +109,9 @@ //! # fn main() -> Result<(), log::kv::Error> { //! use log::kv::{self, Source, Key, value::Visitor}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); -//! +//! //! struct IsNumeric(bool); -//! +//! //! impl<'kvs> Visitor<'kvs> for IsNumeric { //! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { //! self.0 = false; @@ -143,23 +143,23 @@ //! Ok(()) //! } //! } -//! +//! //! // info!("Something of interest"; a = 1); //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! let mut visitor = IsNumeric(false); -//! +//! //! a.visit(&mut visitor)?; -//! +//! //! let is_numeric = visitor.0; -//! +//! //! assert!(is_numeric); //! # Ok(()) //! # } //! ``` //! //! To serialize a value to a format like JSON, you can also use either `serde` or `sval`: -//! +//! //! ``` //! # fn main() -> Result<(), Box> { //! # #[cfg(feature = "serde")] @@ -169,16 +169,16 @@ //! let data = Data { a: 1, b: true, c: "Some data" }; //! # let source = [("a", log::kv::Value::from_serde(&data))]; //! # let record = log::Record::builder().key_values(&source).build(); -//! +//! //! // info!("Something of interest"; a = data); //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! assert_eq!("{\"a\":1,\"b\":true,\"c\":\"Some data\"}", serde_json::to_string(&a)?); //! # } //! # Ok(()) //! # } //! ``` -//! +//! //! The choice of serialization framework depends on the needs of the consumer. //! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. //! Log producers and log consumers don't need to agree on the serialization framework. @@ -187,17 +187,17 @@ //! //! Values can also always be formatted using the standard `Debug` and `Display` //! traits: -//! +//! //! ``` //! # use log::kv::Key; //! # #[derive(Debug)] struct Data { a: i32, b: bool, c: &'static str } //! let data = Data { a: 1, b: true, c: "Some data" }; //! # let source = [("a", log::kv::Value::from_debug(&data))]; //! # let record = log::Record::builder().key_values(&source).build(); -//! +//! //! // info!("Something of interest"; a = data); //! let a = record.key_values().get(Key::from("a")).unwrap(); -//! +//! //! assert_eq!("Data { a: 1, b: true, c: \"Some data\" }", format!("{a:?}")); //! ``` diff --git a/src/kv/source.rs b/src/kv/source.rs index 69544162d..87d955417 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -1,5 +1,5 @@ //! Sources for key-values. -//! +//! //! This module defines the [`Source`] type and supporting APIs for //! working with collections of key-values. @@ -11,7 +11,7 @@ use std::fmt; /// The source may be a single pair, a set of pairs, or a filter over a set of pairs. /// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data /// in a source. -/// +/// /// A source is like an iterator over its key-values, except with a push-based API /// instead of a pull-based one. /// @@ -22,19 +22,19 @@ use std::fmt; /// ``` /// # fn main() -> Result<(), log::kv::Error> { /// use log::kv::{self, Source, Key, Value, source::Visitor}; -/// +/// /// // A `Visitor` that prints all key-values /// // Visitors are fed the key-value pairs of each key-values /// struct Printer; -/// +/// /// impl<'kvs> Visitor<'kvs> for Printer { /// fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { /// println!("{key}: {value}"); -/// +/// /// Ok(()) /// } /// } -/// +/// /// // A source with 3 key-values /// // Common collection types implement the `Source` trait /// let source = &[ @@ -42,7 +42,7 @@ use std::fmt; /// ("b", 2), /// ("c", 3), /// ]; -/// +/// /// // Pass an instance of the `Visitor` to a `Source` to visit it /// source.visit(&mut Printer)?; /// # Ok(()) diff --git a/src/kv/value.rs b/src/kv/value.rs index 1abc01bde..43e682825 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -1,5 +1,5 @@ //! Structured values. -//! +//! //! This module defines the [`Value`] type and supporting APIs for //! capturing and serializing them. @@ -90,9 +90,9 @@ impl<'v> ToValue for Value<'v> { /// For more complex types one of the following traits can be used: /// * [`sval::Value`], requires the `kv_unstable_sval` feature. /// * [`serde::Serialize`], requires the `kv_unstable_serde` feature. -/// +/// /// You don't need a [`Visit`] to serialize values. -/// +/// /// A value can always be serialized using any supported framework, regardless /// of how it was captured. If, for example, a value was captured using its /// `Display` implementation, it will serialize as a string. If it was captured @@ -622,39 +622,57 @@ pub(in crate::kv) mod inner { } fn visit_u64(&mut self, value: u64) -> Result<(), Error> { - self.0.visit_u64(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_u64(value) + .map_err(crate::kv::Error::into_value) } fn visit_i64(&mut self, value: i64) -> Result<(), Error> { - self.0.visit_i64(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_i64(value) + .map_err(crate::kv::Error::into_value) } fn visit_u128(&mut self, value: u128) -> Result<(), Error> { - self.0.visit_u128(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_u128(value) + .map_err(crate::kv::Error::into_value) } fn visit_i128(&mut self, value: i128) -> Result<(), Error> { - self.0.visit_i128(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_i128(value) + .map_err(crate::kv::Error::into_value) } fn visit_f64(&mut self, value: f64) -> Result<(), Error> { - self.0.visit_f64(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_f64(value) + .map_err(crate::kv::Error::into_value) } fn visit_bool(&mut self, value: bool) -> Result<(), Error> { - self.0.visit_bool(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_bool(value) + .map_err(crate::kv::Error::into_value) } fn visit_str(&mut self, value: &str) -> Result<(), Error> { - self.0.visit_str(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_str(value) + .map_err(crate::kv::Error::into_value) } fn visit_borrowed_str(&mut self, value: &'v str) -> Result<(), Error> { - self.0.visit_borrowed_str(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_borrowed_str(value) + .map_err(crate::kv::Error::into_value) } fn visit_char(&mut self, value: char) -> Result<(), Error> { - self.0.visit_char(value).map_err(crate::kv::Error::into_value) + self.0 + .visit_char(value) + .map_err(crate::kv::Error::into_value) } #[cfg(feature = "kv_unstable_std")] @@ -662,7 +680,9 @@ pub(in crate::kv) mod inner { &mut self, err: &(dyn std::error::Error + 'static), ) -> Result<(), Error> { - self.0.visit_error(err).map_err(crate::kv::Error::into_value) + self.0 + .visit_error(err) + .map_err(crate::kv::Error::into_value) } #[cfg(feature = "kv_unstable_std")] @@ -670,7 +690,9 @@ pub(in crate::kv) mod inner { &mut self, err: &'v (dyn std::error::Error + 'static), ) -> Result<(), Error> { - self.0.visit_borrowed_error(err).map_err(crate::kv::Error::into_value) + self.0 + .visit_borrowed_error(err) + .map_err(crate::kv::Error::into_value) } } @@ -873,7 +895,6 @@ pub(in crate::kv) mod inner { F64(f64), I64(i64), U64(u64), - } #[derive(Debug)] diff --git a/src/lib.rs b/src/lib.rs index 6a8e57762..e0577ce97 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,10 +99,10 @@ //! # fn find_a_razor() -> Result { Ok(1) } //! # #[cfg(feature = "kv_unstable_serde")] //! # fn main() { -//! use log::{info, warn, as_serde, as_error}; +//! use log::{info, warn}; //! //! pub fn shave_the_yak(yak: &mut Yak) { -//! info!(target: "yak_events", yak = as_serde!(yak); "Commencing yak shaving"); +//! info!(target: "yak_events", yak:serde = yak; "Commencing yak shaving"); //! //! loop { //! match find_a_razor() { @@ -112,7 +112,7 @@ //! break; //! } //! Err(err) => { -//! warn!(err = as_error!(err); "Unable to locate a razor, retrying"); +//! warn!(err:error = err; "Unable to locate a razor, retrying"); //! } //! } //! } @@ -121,7 +121,7 @@ //! # #[cfg(not(feature = "kv_unstable_serde"))] //! # fn main() {} //! ``` -//! +//! //! See the [`kv`] module documentation for more details. //! //! # Available logging implementations diff --git a/src/macros.rs b/src/macros.rs index 44945f0d9..3e8ba4e73 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,7 +30,7 @@ #[macro_export] macro_rules! log { // log!(target: "my_target", Level::Info, key1 = 42, key2 = true; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ({ + (target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? = $value:expr),+; $($arg:tt)+) => ({ let lvl = $lvl; if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { $crate::__private_api::log::<&_>( @@ -38,7 +38,7 @@ macro_rules! log { lvl, &($target, $crate::__private_api::module_path!(), $crate::__private_api::file!()), $crate::__private_api::line!(), - &[$(($crate::__log_key!($key), &$value)),+] + &[$(($crate::__log_key!($key), $crate::__log_value!(($value)$(:$capture)*))),+] ); } }); @@ -228,6 +228,7 @@ macro_rules! log_enabled { #[doc(hidden)] #[macro_export] +#[cfg(feature = "kv_unstable")] macro_rules! __log_key { // key1 = 42 ($($args:ident)*) => { @@ -238,3 +239,117 @@ macro_rules! __log_key { $($args)* }; } + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_unstable"))] +macro_rules! __log_key { + ($($args:tt)*) => { + compile_error!("key value support requires the `kv_unstable` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_unstable")] +macro_rules! __log_value { + // Default + (($args:expr)) => { + $crate::__log_value!(($args):value) + }; + // ToValue + (($args:expr):value) => { + $crate::__private_api::capture_to_value(&&$args) + }; + // Debug + (($args:expr):?) => { + $crate::__private_api::capture_debug(&&$args) + }; + (($args:expr):debug) => { + $crate::__private_api::capture_debug(&&$args) + }; + // Display + (($args:expr):%) => { + $crate::__private_api::capture_display(&&$args) + }; + (($args:expr):display) => { + $crate::__private_api::capture_display(&&$args) + }; + //Error + (($args:expr):error) => { + $crate::__log_value_error!($args) + }; + // sval::Value + (($args:expr):sval) => { + $crate::__log_value_sval!($args) + }; + // serde::Serialize + (($args:expr):serde) => { + $crate::__log_value_serde!($args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_unstable"))] +macro_rules! __log_value { + ($($args:tt)*) => { + compile_error!("key value support requires the `kv_unstable` feature of `log`") + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_unstable_std"))] +macro_rules! __log_value_error { + ($args:expr) => { + compile_error!("capturing values as `std::error::Error` requites the `kv_unstable_std` feature of `log`") + } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_unstable_sval")] +macro_rules! __log_value_sval { + ($args:expr) => { + $crate::__private_api::capture_sval(&&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_unstable_sval"))] +macro_rules! __log_value_sval { + ($args:expr) => { + compile_error!( + "capturing values as `sval::Value` requites the `kv_unstable_sval` feature of `log`" + ) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_unstable_serde")] +macro_rules! __log_value_serde { + ($args:expr) => { + $crate::__private_api::capture_serde(&&$args) + }; +} + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_unstable_serde"))] +macro_rules! __log_value_serde { + ($args:expr) => { + compile_error!("capturing values as `serde::Serialize` requites the `kv_unstable_serde` feature of `log`") + } +} + +#[doc(hidden)] +#[macro_export] +#[cfg(feature = "kv_unstable_std")] +macro_rules! __log_value_error { + ($args:expr) => { + $crate::__private_api::capture_error(&$args) + }; +} diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 88f37b5d4..7d0d3e853 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -7,6 +7,10 @@ build = "src/build.rs" [features] std = ["log/std"] +kv_unstable = ["log/kv_unstable"] +kv_unstable_std = ["log/kv_unstable_std"] +kv_unstable_sval = ["log/kv_unstable_sval"] +kv_unstable_serde = ["log/kv_unstable_serde"] [dependencies.log] path = ".." diff --git a/tests/macros.rs b/tests/macros.rs index 3afcf88da..47cc957d9 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -250,6 +250,53 @@ fn kv_common_value_types() { ); } +#[test] +#[cfg(feature = "kv_unstable")] +fn kv_debug() { + all_log_macros!( + a:? = 42, + b:debug = 42; + "hello world" + ); +} + +#[test] +#[cfg(feature = "kv_unstable")] +fn kv_display() { + all_log_macros!( + a:% = 42, + b:display = 42; + "hello world" + ); +} + +#[test] +#[cfg(feature = "kv_unstable_std")] +fn kv_error() { + all_log_macros!( + a:error = std::io::Error::new(std::io::ErrorKind::Other, "an error"); + "hello world" + ); +} + +#[test] +#[cfg(feature = "kv_unstable_sval")] +fn kv_sval() { + all_log_macros!( + a:sval = 42; + "hello world" + ); +} + +#[test] +#[cfg(feature = "kv_unstable_serde")] +fn kv_serde() { + all_log_macros!( + a:serde = 42; + "hello world" + ); +} + /// Some and None (from Option) are used in the macros. #[derive(Debug)] enum Type { From 6b483c62cd8f4072117722e113334fd0782a535a Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 13 Feb 2024 20:17:58 +1000 Subject: [PATCH 13/26] make capturing docs easier to read --- src/kv/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 18c2dbd9d..8a0eb1a56 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -45,13 +45,13 @@ //! //! The following capturing modifiers are supported: //! -//! - `:?`: `Debug`. -//! - `:debug`: `Debug`. -//! - `:%`: `Display`. -//! - `:display`: `Display`. -//! - `:error`: `std::error::Error` (requires the `kv_unstable_error` feature). -//! - `:sval`: `sval::Value` (requires the `kv_unstable_sval` feature). -//! - `:serde`: `serde::Serialize` (requires the `kv_unstable_serde` feature). +//! - `:?` will capture the value using `Debug`. +//! - `:debug` will capture the value using `Debug`. +//! - `:%` will capture the value using `Display`. +//! - `:display` will capture the value using `Display`. +//! - `:error` will capture the value using `std::error::Error` (requires the `kv_unstable_error` feature). +//! - `:sval` will capture the value using `sval::Value` (requires the `kv_unstable_sval` feature). +//! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_unstable_serde` feature). //! //! ## Working with key-values on log records //! From d8dc6a834bae3bb55b0121e78adcfb31ffb64681 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 13 Feb 2024 20:30:05 +1000 Subject: [PATCH 14/26] rename source::Visitor to VisitSource and value::Visitor to VisitValue --- src/kv/mod.rs | 19 +++++++-------- src/kv/source.rs | 62 ++++++++++++++++++++++++------------------------ src/kv/value.rs | 40 +++++++++++++++---------------- src/lib.rs | 8 +++---- 4 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 8a0eb1a56..6d38dbe50 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -71,18 +71,18 @@ //! # } //! ``` //! -//! All key-values can also be enumerated using a [`source::Visitor`]: +//! All key-values can also be enumerated using a [`VisitSource`]: //! //! ``` //! # fn main() -> Result<(), log::kv::Error> { //! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); //! use std::collections::BTreeMap; //! -//! use log::kv::{self, Source, Key, Value, source::Visitor}; +//! use log::kv::{self, Source, Key, Value, VisitSource}; //! //! struct Collect<'kvs>(BTreeMap, Value<'kvs>>); //! -//! impl<'kvs> Visitor<'kvs> for Collect<'kvs> { +//! impl<'kvs> VisitSource<'kvs> for Collect<'kvs> { //! fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { //! self.0.insert(key, value); //! @@ -125,17 +125,17 @@ //! # } //! ``` //! -//! Values also have their own [`value::Visitor`] type. Visitors are a lightweight +//! Values also have their own [`VisitValue`] type. Value visitors are a lightweight //! API for working with primitives types: //! //! ``` //! # fn main() -> Result<(), log::kv::Error> { -//! use log::kv::{self, Source, Key, value::Visitor}; +//! use log::kv::{self, Source, Key, VisitValue}; //! # let record = log::Record::builder().key_values(&[("a", 1)]).build(); //! //! struct IsNumeric(bool); //! -//! impl<'kvs> Visitor<'kvs> for IsNumeric { +//! impl<'kvs> VisitValue<'kvs> for IsNumeric { //! fn visit_any(&mut self, _value: kv::Value) -> Result<(), kv::Error> { //! self.0 = false; //! Ok(()) @@ -230,12 +230,9 @@ mod error; mod key; pub mod source; - pub mod value; pub use self::error::Error; pub use self::key::{Key, ToKey}; -pub use self::source::{Source, Visitor}; - -#[doc(inline)] -pub use self::value::{ToValue, Value}; +pub use self::source::{Source, VisitSource}; +pub use self::value::{ToValue, Value, VisitValue}; diff --git a/src/kv/source.rs b/src/kv/source.rs index 87d955417..df4fe1bd8 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -9,7 +9,7 @@ use std::fmt; /// A source of key-values. /// /// The source may be a single pair, a set of pairs, or a filter over a set of pairs. -/// Use the [`Visitor`](trait.Visitor.html) trait to inspect the structured data +/// Use the [`VisitSource`](trait.VisitSource.html) trait to inspect the structured data /// in a source. /// /// A source is like an iterator over its key-values, except with a push-based API @@ -21,13 +21,13 @@ use std::fmt; /// /// ``` /// # fn main() -> Result<(), log::kv::Error> { -/// use log::kv::{self, Source, Key, Value, source::Visitor}; +/// use log::kv::{self, Source, Key, Value, source::VisitSource}; /// -/// // A `Visitor` that prints all key-values -/// // Visitors are fed the key-value pairs of each key-values +/// // A `VisitSource` that prints all key-values +/// // VisitSources are fed the key-value pairs of each key-values /// struct Printer; /// -/// impl<'kvs> Visitor<'kvs> for Printer { +/// impl<'kvs> VisitSource<'kvs> for Printer { /// fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), kv::Error> { /// println!("{key}: {value}"); /// @@ -43,7 +43,7 @@ use std::fmt; /// ("c", 3), /// ]; /// -/// // Pass an instance of the `Visitor` to a `Source` to visit it +/// // Pass an instance of the `VisitSource` to a `Source` to visit it /// source.visit(&mut Printer)?; /// # Ok(()) /// # } @@ -59,7 +59,7 @@ pub trait Source { /// /// A source should yield the same key-values to a subsequent visitor unless /// that visitor itself fails. - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error>; + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error>; /// Get the value for a given key. /// @@ -95,7 +95,7 @@ fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option>, } - impl<'k, 'kvs> Visitor<'kvs> for Get<'k, 'kvs> { + impl<'k, 'kvs> VisitSource<'kvs> for Get<'k, 'kvs> { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { if self.key == key { self.found = Some(value); @@ -115,7 +115,7 @@ fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option usize { struct Count(usize); - impl<'kvs> Visitor<'kvs> for Count { + impl<'kvs> VisitSource<'kvs> for Count { fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> { self.0 += 1; @@ -132,7 +132,7 @@ impl<'a, T> Source for &'a T where T: Source + ?Sized, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -150,7 +150,7 @@ where K: ToKey, V: ToValue, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { visitor.visit_pair(self.0.to_key(), self.1.to_value()) } @@ -171,7 +171,7 @@ impl Source for [S] where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { for source in self { source.visit(visitor)?; } @@ -198,7 +198,7 @@ impl Source for [S; N] where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { Source::visit(self as &[_], visitor) } @@ -215,7 +215,7 @@ impl Source for Option where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { if let Some(source) = self { source.visit(visitor)?; } @@ -233,42 +233,42 @@ where } /// A visitor for the key-value pairs in a [`Source`](trait.Source.html). -pub trait Visitor<'kvs> { +pub trait VisitSource<'kvs> { /// Visit a key-value pair. fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error>; } -impl<'a, 'kvs, T> Visitor<'kvs> for &'a mut T +impl<'a, 'kvs, T> VisitSource<'kvs> for &'a mut T where - T: Visitor<'kvs> + ?Sized, + T: VisitSource<'kvs> + ?Sized, { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { (**self).visit_pair(key, value) } } -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugMap<'a, 'b> { +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugMap<'a, 'b> { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { self.entry(&key, &value); Ok(()) } } -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugList<'a, 'b> { +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugList<'a, 'b> { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { self.entry(&(key, value)); Ok(()) } } -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugSet<'a, 'b> { +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugSet<'a, 'b> { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { self.entry(&(key, value)); Ok(()) } } -impl<'a, 'b: 'a, 'kvs> Visitor<'kvs> for fmt::DebugTuple<'a, 'b> { +impl<'a, 'b: 'a, 'kvs> VisitSource<'kvs> for fmt::DebugTuple<'a, 'b> { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { self.field(&key); self.field(&value); @@ -289,7 +289,7 @@ mod std_support { where S: Source + ?Sized, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -306,7 +306,7 @@ mod std_support { where S: Source + ?Sized, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -323,7 +323,7 @@ mod std_support { where S: Source + ?Sized, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -340,7 +340,7 @@ mod std_support { where S: Source, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { Source::visit(&**self, visitor) } @@ -353,9 +353,9 @@ mod std_support { } } - impl<'kvs, V> Visitor<'kvs> for Box + impl<'kvs, V> VisitSource<'kvs> for Box where - V: Visitor<'kvs> + ?Sized, + V: VisitSource<'kvs> + ?Sized, { fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> { (**self).visit_pair(key, value) @@ -368,7 +368,7 @@ mod std_support { V: ToValue, S: BuildHasher, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { for (key, value) in self { visitor.visit_pair(key.to_key(), value.to_value())?; } @@ -389,7 +389,7 @@ mod std_support { K: ToKey + Borrow + Ord, V: ToValue, { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { for (key, value) in self { visitor.visit_pair(key.to_key(), value.to_value())?; } @@ -472,7 +472,7 @@ mod tests { #[test] fn visitor_is_object_safe() { - fn _check(_: &dyn Visitor) {} + fn _check(_: &dyn VisitSource) {} } #[test] @@ -483,7 +483,7 @@ mod tests { } impl Source for OnePair { - fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> { + fn visit<'kvs>(&'kvs self, visitor: &mut dyn VisitSource<'kvs>) -> Result<(), Error> { visitor.visit_pair(self.key.to_key(), self.value.to_value()) } } diff --git a/src/kv/value.rs b/src/kv/value.rs index 971bf6bbf..a4dd8f618 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -102,7 +102,7 @@ impl<'v> ToValue for Value<'v> { /// /// For basic types the [`Value::visit`] method can be used to extract the /// underlying typed value. However this is limited in the amount of types -/// supported (see the [`Visitor`] trait methods). +/// supported (see the [`VisitValue`] trait methods). /// /// For more complex types one of the following traits can be used: /// * `sval::Value`, requires the `kv_unstable_sval` feature. @@ -213,7 +213,7 @@ impl<'v> Value<'v> { /// /// When the `kv_unstable_serde` or `kv_unstable_sval` features are enabled, you can also /// serialize a value using its `Serialize` or `Value` implementation. - pub fn visit(&self, visitor: impl Visitor<'v>) -> Result<(), Error> { + pub fn visit(&self, visitor: impl VisitValue<'v>) -> Result<(), Error> { inner::visit(&self.inner, visitor) } } @@ -436,20 +436,20 @@ mod std_support { /// A visitor for a [`Value`]. /// -/// Also see [`Value`'s documentation on seralization]. Visitors are a simple alternative -/// to a more fully-featured serialization framework like `serde` or `sval`. A visitor -/// can differentiate primitive types through methods like [`Visitor::visit_bool`] and -/// [`Visitor::visit_str`], but more complex types like maps and sequences -/// will fallthrough to [`Visitor::visit_any`]. +/// Also see [`Value`'s documentation on seralization]. Value visitors are a simple alternative +/// to a more fully-featured serialization framework like `serde` or `sval`. A value visitor +/// can differentiate primitive types through methods like [`VisitValue::visit_bool`] and +/// [`VisitValue::visit_str`], but more complex types like maps and sequences +/// will fallthrough to [`VisitValue::visit_any`]. /// /// If you're trying to serialize a value to a format like JSON, you can use either `serde` /// or `sval` directly with the value. You don't need a visitor. /// /// [`Value`'s documentation on seralization]: Value#serialization -pub trait Visitor<'v> { +pub trait VisitValue<'v> { /// Visit a `Value`. /// - /// This is the only required method on `Visitor` and acts as a fallback for any + /// This is the only required method on `VisitValue` 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. @@ -522,9 +522,9 @@ pub trait Visitor<'v> { } } -impl<'a, 'v, T: ?Sized> Visitor<'v> for &'a mut T +impl<'a, 'v, T: ?Sized> VisitValue<'v> for &'a mut T where - T: Visitor<'v>, + T: VisitValue<'v>, { fn visit_any(&mut self, value: Value) -> Result<(), Error> { (**self).visit_any(value) @@ -607,12 +607,12 @@ pub(in crate::kv) mod inner { inner.to_test_token() } - pub fn visit<'v>(inner: &Inner<'v>, visitor: impl Visitor<'v>) -> Result<(), crate::kv::Error> { - struct InnerVisitor(V); + pub fn visit<'v>(inner: &Inner<'v>, visitor: impl VisitValue<'v>) -> Result<(), crate::kv::Error> { + struct InnerVisitValue(V); - impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitor + impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitValue where - V: Visitor<'v>, + V: VisitValue<'v>, { fn visit_any(&mut self, value: value_bag::ValueBag) -> Result<(), Error> { self.0 @@ -700,7 +700,7 @@ pub(in crate::kv) mod inner { } inner - .visit(&mut InnerVisitor(visitor)) + .visit(&mut InnerVisitValue(visitor)) .map_err(crate::kv::Error::from_value) } } @@ -717,7 +717,7 @@ pub(in crate::kv) mod inner { the same `value_bag`-based conversion must also. Of particular note here are floats to ints; they're based on the standard library's `TryInto` conversions, which need to be convert to `i32` or `u32`, and then to `f64`. - 2. Visitors should always be called in the same way. If a particular type of value calls `visit_i64`, + 2. VisitValues should always be called in the same way. If a particular type of value calls `visit_i64`, then the same `value_bag`-based visitor must also. */ use super::*; @@ -1018,7 +1018,7 @@ pub(in crate::kv) mod inner { pub fn visit<'v>( inner: &Inner<'v>, - mut visitor: impl Visitor<'v>, + mut visitor: impl VisitValue<'v>, ) -> Result<(), crate::kv::Error> { match inner { Inner::None => visitor.visit_null(), @@ -1216,7 +1216,7 @@ pub(crate) mod tests { fn test_visit_integer() { struct Extract(Option); - impl<'v> Visitor<'v> for Extract { + impl<'v> VisitValue<'v> for Extract { fn visit_any(&mut self, value: Value) -> Result<(), Error> { unimplemented!("unexpected value: {value:?}") } @@ -1238,7 +1238,7 @@ pub(crate) mod tests { fn test_visit_borrowed_str() { struct Extract<'v>(Option<&'v str>); - impl<'v> Visitor<'v> for Extract<'v> { + impl<'v> VisitValue<'v> for Extract<'v> { fn visit_any(&mut self, value: Value) -> Result<(), Error> { unimplemented!("unexpected value: {value:?}") } diff --git a/src/lib.rs b/src/lib.rs index e0577ce97..8abbfcb2e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1760,13 +1760,13 @@ mod tests { #[cfg(feature = "kv_unstable")] fn test_record_key_values_builder() { use super::Record; - use crate::kv::{self, Visitor}; + use crate::kv::{self, VisitSource}; - struct TestVisitor { + struct TestVisitSource { seen_pairs: usize, } - impl<'kvs> Visitor<'kvs> for TestVisitor { + impl<'kvs> VisitSource<'kvs> for TestVisitSource { fn visit_pair( &mut self, _: kv::Key<'kvs>, @@ -1780,7 +1780,7 @@ mod tests { let kvs: &[(&str, i32)] = &[("a", 1), ("b", 2)]; let record_test = Record::builder().key_values(&kvs).build(); - let mut visitor = TestVisitor { seen_pairs: 0 }; + let mut visitor = TestVisitSource { seen_pairs: 0 }; record_test.key_values().visit(&mut visitor).unwrap(); From f6b89c0377c04b3ba4e5c090a9ca603048ddf64c Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 16 Feb 2024 15:08:13 +1000 Subject: [PATCH 15/26] update and clarify docs, make internal kv modules private --- src/__private_api.rs | 6 +++++- src/kv/mod.rs | 36 +++++++++++++++++++++++------------- src/kv/source.rs | 3 +-- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/__private_api.rs b/src/__private_api.rs index 8ea869958..ce08a5e98 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -93,7 +93,11 @@ mod kv_support { pub type Value<'a> = kv::Value<'a>; - pub fn capture_to_value<'a, V: kv::value::ToValue + ?Sized>(v: &'a &'a V) -> Value<'a> { + // NOTE: Many functions here accept a double reference &&V + // This is so V itself can be ?Sized, while still letting us + // erase it to some dyn Trait (because &T is sized) + + pub fn capture_to_value<'a, V: kv::ToValue + ?Sized>(v: &'a &'a V) -> Value<'a> { v.to_value() } diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 6d38dbe50..a0ef2b9c3 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -15,9 +15,8 @@ //! # Structured logging in `log` //! //! Structured logging enhances traditional text-based log records with user-defined -//! attributes. Structured logs can be analyzed using a variety of tranditional -//! data processing techniques, without needing to find and parse attributes from -//! unstructured text first. +//! attributes. Structured logs can be analyzed using a variety of data processing +//! techniques, without needing to find and parse attributes from unstructured text first. //! //! In `log`, user-defined attributes are part of a [`Source`] on the log record. //! Each attribute is a key-value; a pair of [`Key`] and [`Value`]. Keys are strings @@ -49,13 +48,13 @@ //! - `:debug` will capture the value using `Debug`. //! - `:%` will capture the value using `Display`. //! - `:display` will capture the value using `Display`. -//! - `:error` will capture the value using `std::error::Error` (requires the `kv_unstable_error` feature). +//! - `:error` will capture the value using `std::error::Error` (requires the `kv_unstable_std` feature). //! - `:sval` will capture the value using `sval::Value` (requires the `kv_unstable_sval` feature). //! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_unstable_serde` feature). //! //! ## Working with key-values on log records //! -//! Use the [`LogRecord::key_values`] method to access key-values. +//! Use the [`Record::key_values`](../struct.Record.html#method.key_values) method to access key-values. //! //! Individual values can be pulled from the source by their key: //! @@ -75,7 +74,6 @@ //! //! ``` //! # fn main() -> Result<(), log::kv::Error> { -//! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); //! use std::collections::BTreeMap; //! //! use log::kv::{self, Source, Key, Value, VisitSource}; @@ -92,6 +90,7 @@ //! //! let mut visitor = Collect(BTreeMap::new()); //! +//! # let record = log::Record::builder().key_values(&[("a", 1), ("b", 2), ("c", 3)]).build(); //! // info!(a = 1, b = 2, c = 3; "Something of interest"); //! //! record.key_values().visit(&mut visitor)?; @@ -189,11 +188,16 @@ //! # #[cfg(feature = "serde")] //! # { //! # use log::kv::Key; -//! # #[derive(serde::Serialize)] struct Data { a: i32, b: bool, c: &'static str } +//! #[derive(serde::Serialize)] +//! struct Data { +//! a: i32, b: bool, +//! c: &'static str, +//! } +//! //! let data = Data { a: 1, b: true, c: "Some data" }; +//! //! # let source = [("a", log::kv::Value::from_serde(&data))]; //! # let record = log::Record::builder().key_values(&source).build(); -//! //! // info!(a = data; "Something of interest"); //! //! let a = record.key_values().get(Key::from("a")).unwrap(); @@ -208,18 +212,24 @@ //! If you're in a no-std environment, you can use `sval`. In other cases, you can use `serde`. //! Log producers and log consumers don't need to agree on the serialization framework. //! A value can be captured using its `serde::Serialize` implementation and still be serialized -//! through `sval` without losing any structure. +//! through `sval` without losing any structure or data. //! //! Values can also always be formatted using the standard `Debug` and `Display` //! traits: //! //! ``` //! # use log::kv::Key; -//! # #[derive(Debug)] struct Data { a: i32, b: bool, c: &'static str } +//! # #[derive(Debug)] +//! struct Data { +//! a: i32, +//! b: bool, +//! c: &'static str, +//! } +//! //! let data = Data { a: 1, b: true, c: "Some data" }; +//! //! # let source = [("a", log::kv::Value::from_debug(&data))]; //! # let record = log::Record::builder().key_values(&source).build(); -//! //! // info!(a = data; "Something of interest"); //! //! let a = record.key_values().get(Key::from("a")).unwrap(); @@ -229,8 +239,8 @@ mod error; mod key; -pub mod source; -pub mod value; +mod source; +mod value; pub use self::error::Error; pub use self::key::{Key, ToKey}; diff --git a/src/kv/source.rs b/src/kv/source.rs index df4fe1bd8..d3ba3f2fa 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -81,8 +81,7 @@ pub trait Source { /// A source that knows the number of key-values upfront may provide a more /// efficient implementation. /// - /// A subsequent call to `visit` should yield the same number of key-values - /// to the visitor, unless that visitor fails part way through. + /// A subsequent call to `visit` should yield the same number of key-values. fn count(&self) -> usize { count_default(self) } From 6d9e98aa03650e7770bd4bb67391e765d36a77fd Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 16 Feb 2024 15:09:18 +1000 Subject: [PATCH 16/26] run fmt --- src/kv/mod.rs | 10 +++++----- src/kv/value.rs | 5 ++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/kv/mod.rs b/src/kv/mod.rs index a0ef2b9c3..f7f91f4b9 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -15,7 +15,7 @@ //! # Structured logging in `log` //! //! Structured logging enhances traditional text-based log records with user-defined -//! attributes. Structured logs can be analyzed using a variety of data processing +//! attributes. Structured logs can be analyzed using a variety of data processing //! techniques, without needing to find and parse attributes from unstructured text first. //! //! In `log`, user-defined attributes are part of a [`Source`] on the log record. @@ -32,18 +32,18 @@ //! # use log::info; //! info!(a = 1; "Something of interest"); //! ``` -//! +//! //! Values are capturing using the [`ToValue`] trait by default. To capture a value //! using a different trait implementation, use a modifier after its key. Here's how //! the same example can capture `a` using its `Debug` implementation instead: -//! +//! //! ``` //! # use log::info; //! info!(a:? = 1; "Something of interest"); //! ``` -//! +//! //! The following capturing modifiers are supported: -//! +//! //! - `:?` will capture the value using `Debug`. //! - `:debug` will capture the value using `Debug`. //! - `:%` will capture the value using `Display`. diff --git a/src/kv/value.rs b/src/kv/value.rs index a4dd8f618..646a22d9d 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -607,7 +607,10 @@ pub(in crate::kv) mod inner { inner.to_test_token() } - pub fn visit<'v>(inner: &Inner<'v>, visitor: impl VisitValue<'v>) -> Result<(), crate::kv::Error> { + pub fn visit<'v>( + inner: &Inner<'v>, + visitor: impl VisitValue<'v>, + ) -> Result<(), crate::kv::Error> { struct InnerVisitValue(V); impl<'v, V> value_bag::visit::Visit<'v> for InnerVisitValue From 44b8e990596b3d0cd21f669e62d9cb0790ea827e Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 16 Feb 2024 15:14:35 +1000 Subject: [PATCH 17/26] fix up some warnings --- src/__private_api.rs | 2 -- src/kv/mod.rs | 1 + src/kv/source.rs | 2 +- src/kv/value.rs | 5 ----- 4 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/__private_api.rs b/src/__private_api.rs index ce08a5e98..5686142c8 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -87,8 +87,6 @@ pub fn enabled(level: Level, target: &str) -> bool { #[cfg(feature = "kv_unstable")] mod kv_support { - use super::*; - use crate::kv; pub type Value<'a> = kv::Value<'a>; diff --git a/src/kv/mod.rs b/src/kv/mod.rs index f7f91f4b9..84d392cd4 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -66,6 +66,7 @@ //! // info!(a = 1; "Something of interest"); //! //! let a: Value = record.key_values().get(Key::from("a")).unwrap(); +//! assert_eq!(1, a.to_i64().unwrap()); //! # Ok(()) //! # } //! ``` diff --git a/src/kv/source.rs b/src/kv/source.rs index d3ba3f2fa..33d5a1a73 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -21,7 +21,7 @@ use std::fmt; /// /// ``` /// # fn main() -> Result<(), log::kv::Error> { -/// use log::kv::{self, Source, Key, Value, source::VisitSource}; +/// use log::kv::{self, Source, Key, Value, VisitSource}; /// /// // A `VisitSource` that prints all key-values /// // VisitSources are fed the key-value pairs of each key-values diff --git a/src/kv/value.rs b/src/kv/value.rs index 646a22d9d..2e0d423e6 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -602,11 +602,6 @@ pub(in crate::kv) mod inner { #[cfg(test)] pub use value_bag::test::TestToken as Token; - #[cfg(test)] - pub fn to_test_token<'v>(inner: &Inner<'v>) -> Token { - inner.to_test_token() - } - pub fn visit<'v>( inner: &Inner<'v>, visitor: impl VisitValue<'v>, From a6c40954b1767a53cb0a80a98d00adfde889dec9 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 16 Feb 2024 15:16:54 +1000 Subject: [PATCH 18/26] add a few more notes to the source --- src/macros.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 3e8ba4e73..74de7f892 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,7 +29,7 @@ /// ``` #[macro_export] macro_rules! log { - // log!(target: "my_target", Level::Info, key1 = 42, key2 = true; "a {} event", "log"); + // log!(target: "my_target", Level::Info, key1:? = 42, key2 = true; "a {} event", "log"); (target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? = $value:expr),+; $($arg:tt)+) => ({ let lvl = $lvl; if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { @@ -226,6 +226,9 @@ macro_rules! log_enabled { }; } +// These macros use a pattern of #[cfg]s to produce nicer error +// messages when log features aren't available + #[doc(hidden)] #[macro_export] #[cfg(feature = "kv_unstable")] From 52460f9928541f9d014493d846a5fd0e90f9ba8a Mon Sep 17 00:00:00 2001 From: KodrAus Date: Fri, 16 Feb 2024 15:40:52 +1000 Subject: [PATCH 19/26] stabilize the kv features --- .github/workflows/main.yml | 36 ++++++++++++++++++---------------- Cargo.toml | 18 ++++++++++------- README.md | 2 +- benches/value.rs | 2 +- src/__private_api.rs | 20 +++++++++---------- src/kv/error.rs | 4 ++-- src/kv/key.rs | 4 ++-- src/kv/mod.rs | 16 ++++++--------- src/kv/value.rs | 40 +++++++++++++++++++------------------- src/lib.rs | 26 ++++++++++++------------- src/macros.rs | 40 ++++++++++++++++++++------------------ tests/Cargo.toml | 8 ++++---- tests/macros.rs | 30 ++++++++++++++-------------- 13 files changed, 124 insertions(+), 122 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3fc335e73..3c263a434 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,10 +44,10 @@ jobs: - run: cargo test --verbose --all-features - run: cargo test --verbose --features serde - run: cargo test --verbose --features std - - run: cargo test --verbose --features kv_unstable - - run: cargo test --verbose --features kv_unstable_sval - - run: cargo test --verbose --features kv_unstable_serde - - run: cargo test --verbose --features "kv_unstable kv_unstable_std kv_unstable_sval kv_unstable_serde" + - run: cargo test --verbose --features kv + - run: cargo test --verbose --features kv_sval + - run: cargo test --verbose --features kv_serde + - run: cargo test --verbose --features "kv kv_std kv_sval kv_serde" - run: cargo run --verbose --manifest-path test_max_level_features/Cargo.toml - run: cargo run --verbose --manifest-path test_max_level_features/Cargo.toml --release @@ -103,12 +103,12 @@ jobs: run: | rustup update nightly --no-self-update rustup default nightly - - run: cargo build --verbose -Z avoid-dev-deps --features kv_unstable - - run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable std" - - run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable kv_unstable_sval" - - run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable kv_unstable_serde" - - run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable kv_unstable_std" - - run: cargo build --verbose -Z avoid-dev-deps --features "kv_unstable kv_unstable_sval kv_unstable_serde" + - run: cargo build --verbose -Z avoid-dev-deps --features kv + - run: cargo build --verbose -Z avoid-dev-deps --features "kv std" + - run: cargo build --verbose -Z avoid-dev-deps --features "kv kv_sval" + - run: cargo build --verbose -Z avoid-dev-deps --features "kv kv_serde" + - run: cargo build --verbose -Z avoid-dev-deps --features "kv kv_std" + - run: cargo build --verbose -Z avoid-dev-deps --features "kv kv_sval kv_serde" minimalv: name: Minimal versions @@ -119,12 +119,12 @@ jobs: run: | rustup update nightly --no-self-update rustup default nightly - - run: cargo build --verbose -Z minimal-versions --features kv_unstable - - run: cargo build --verbose -Z minimal-versions --features "kv_unstable std" - - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_sval" - - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_serde" - - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_std" - - run: cargo build --verbose -Z minimal-versions --features "kv_unstable kv_unstable_sval kv_unstable_serde" + - run: cargo build --verbose -Z minimal-versions --features kv + - run: cargo build --verbose -Z minimal-versions --features "kv std" + - run: cargo build --verbose -Z minimal-versions --features "kv kv_sval" + - run: cargo build --verbose -Z minimal-versions --features "kv kv_serde" + - run: cargo build --verbose -Z minimal-versions --features "kv kv_std" + - run: cargo build --verbose -Z minimal-versions --features "kv kv_sval kv_serde" msrv: name: MSRV @@ -135,7 +135,9 @@ jobs: run: | rustup update 1.60.0 --no-self-update rustup default 1.60.0 - - run: cargo test --verbose --manifest-path tests/Cargo.toml + - run: | + cargo test --verbose --manifest-path tests/Cargo.toml + cargo test --verbose --manifest-path tests/Cargo.toml --features kv embedded: name: Embedded diff --git a/Cargo.toml b/Cargo.toml index c30944c56..5992d0a9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.60.0" edition = "2021" [package.metadata.docs.rs] -features = ["std", "serde", "kv_unstable_std", "kv_unstable_sval", "kv_unstable_serde"] +features = ["std", "serde", "kv_std", "kv_sval", "kv_serde"] [[test]] name = "filters" @@ -46,12 +46,16 @@ release_max_level_trace = [] std = [] -# requires the latest stable -# this will have a tighter MSRV before stabilization -kv_unstable = [] -kv_unstable_sval = ["kv_unstable", "value-bag/sval", "sval", "sval_ref"] -kv_unstable_std = ["std", "kv_unstable", "value-bag/error"] -kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"] +kv = [] +kv_sval = ["kv", "value-bag/sval", "sval", "sval_ref"] +kv_std = ["std", "kv", "value-bag/error"] +kv_serde = ["kv_std", "value-bag/serde", "serde"] + +# Legacy: use `kv_*` instead +kv_unstable = ["kv"] +kv_unstable_sval = ["kv_sval"] +kv_unstable_std = ["kv_std"] +kv_unstable_serde = ["kv_serde"] [dependencies] serde = { version = "1.0", optional = true, default-features = false } diff --git a/README.md b/README.md index 7b188719f..01fd30875 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ The executable itself may use the `log` crate to log as well. ## Structured logging -If you enable the `kv_unstable` feature, you can associate structured data with your log records: +If you enable the `kv` feature, you can associate structured data with your log records: ```rust use log::{info, trace, warn}; diff --git a/benches/value.rs b/benches/value.rs index e43be50f7..3d0f18bfe 100644 --- a/benches/value.rs +++ b/benches/value.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "kv_unstable")] +#![cfg(feature = "kv")] #![feature(test)] use log::kv::Value; diff --git a/src/__private_api.rs b/src/__private_api.rs index 5686142c8..fd0a5a762 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -5,7 +5,7 @@ use crate::{Level, Metadata, Record}; use std::fmt::Arguments; pub use std::{file, format_args, line, module_path, stringify}; -#[cfg(not(feature = "kv_unstable"))] +#[cfg(not(feature = "kv"))] pub type Value<'a> = &'a str; mod sealed { @@ -40,11 +40,9 @@ fn log_impl( line: u32, kvs: Option<&[(&str, Value)]>, ) { - #[cfg(not(feature = "kv_unstable"))] + #[cfg(not(feature = "kv"))] if kvs.is_some() { - panic!( - "key-value support is experimental and must be enabled using the `kv_unstable` feature" - ) + panic!("key-value support is experimental and must be enabled using the `kv` feature") } let mut builder = Record::builder(); @@ -57,7 +55,7 @@ fn log_impl( .file_static(Some(file)) .line(Some(line)); - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] builder.key_values(&kvs); crate::logger().log(&builder.build()); @@ -85,7 +83,7 @@ pub fn enabled(level: Level, target: &str) -> bool { crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) } -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] mod kv_support { use crate::kv; @@ -107,21 +105,21 @@ mod kv_support { Value::from_display(v) } - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] pub fn capture_error<'a>(v: &'a (dyn std::error::Error + 'static)) -> Value<'a> { Value::from_dyn_error(v) } - #[cfg(feature = "kv_unstable_sval")] + #[cfg(feature = "kv_sval")] pub fn capture_sval<'a, V: sval::Value + ?Sized>(v: &'a &'a V) -> Value<'a> { Value::from_sval(v) } - #[cfg(feature = "kv_unstable_serde")] + #[cfg(feature = "kv_serde")] pub fn capture_serde<'a, V: serde::Serialize + ?Sized>(v: &'a &'a V) -> Value<'a> { Value::from_serde(v) } } -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] pub use self::kv_support::*; diff --git a/src/kv/error.rs b/src/kv/error.rs index d654be780..7efa5af36 100644 --- a/src/kv/error.rs +++ b/src/kv/error.rs @@ -37,9 +37,9 @@ impl Error { pub(super) fn into_value(self) -> crate::kv::value::inner::Error { match self.inner { Inner::Value(err) => err, - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] _ => crate::kv::value::inner::Error::boxed(self), - #[cfg(not(feature = "kv_unstable_std"))] + #[cfg(not(feature = "kv_std"))] _ => crate::kv::value::inner::Error::msg("error inspecting a value"), } } diff --git a/src/kv/key.rs b/src/kv/key.rs index fb58008c8..9a64b956f 100644 --- a/src/kv/key.rs +++ b/src/kv/key.rs @@ -93,7 +93,7 @@ mod std_support { } } -#[cfg(feature = "kv_unstable_sval")] +#[cfg(feature = "kv_sval")] mod sval_support { use super::*; @@ -116,7 +116,7 @@ mod sval_support { } } -#[cfg(feature = "kv_unstable_serde")] +#[cfg(feature = "kv_serde")] mod serde_support { use super::*; diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 84d392cd4..44f7b06df 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -1,15 +1,11 @@ -//! **UNSTABLE:** Structured logging. +//! Structured logging. //! -//! This module is unstable and breaking changes may be made -//! at any time. See [the tracking issue](https://github.com/rust-lang-nursery/log/issues/328) -//! for more details. -//! -//! Add the `kv_unstable` feature to your `Cargo.toml` to enable +//! Add the `kv` feature to your `Cargo.toml` to enable //! this module: //! //! ```toml //! [dependencies.log] -//! features = ["kv_unstable"] +//! features = ["kv"] //! ``` //! //! # Structured logging in `log` @@ -48,9 +44,9 @@ //! - `:debug` will capture the value using `Debug`. //! - `:%` will capture the value using `Display`. //! - `:display` will capture the value using `Display`. -//! - `:error` will capture the value using `std::error::Error` (requires the `kv_unstable_std` feature). -//! - `:sval` will capture the value using `sval::Value` (requires the `kv_unstable_sval` feature). -//! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_unstable_serde` feature). +//! - `:error` will capture the value using `std::error::Error` (requires the `kv_std` feature). +//! - `:sval` will capture the value using `sval::Value` (requires the `kv_sval` feature). +//! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_serde` feature). //! //! ## Working with key-values on log records //! diff --git a/src/kv/value.rs b/src/kv/value.rs index 2e0d423e6..6bfff4e4e 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -105,8 +105,8 @@ impl<'v> ToValue for Value<'v> { /// supported (see the [`VisitValue`] trait methods). /// /// For more complex types one of the following traits can be used: -/// * `sval::Value`, requires the `kv_unstable_sval` feature. -/// * `serde::Serialize`, requires the `kv_unstable_serde` feature. +/// * `sval::Value`, requires the `kv_sval` feature. +/// * `serde::Serialize`, requires the `kv_serde` feature. /// /// You don't need a visitor to serialize values through `serde` or `sval`. /// @@ -149,7 +149,7 @@ impl<'v> Value<'v> { } /// Get a value from a type implementing `serde::Serialize`. - #[cfg(feature = "kv_unstable_serde")] + #[cfg(feature = "kv_serde")] pub fn from_serde(value: &'v T) -> Self where T: serde::Serialize, @@ -160,7 +160,7 @@ impl<'v> Value<'v> { } /// Get a value from a type implementing `sval::Value`. - #[cfg(feature = "kv_unstable_sval")] + #[cfg(feature = "kv_sval")] pub fn from_sval(value: &'v T) -> Self where T: sval::Value, @@ -185,7 +185,7 @@ impl<'v> Value<'v> { } /// Get a value from a dynamic error. - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] pub fn from_dyn_error(err: &'v (dyn std::error::Error + 'static)) -> Self { Value { inner: inner::Inner::from_dyn_error(err), @@ -211,7 +211,7 @@ impl<'v> Value<'v> { /// Inspect this value using a simple visitor. /// - /// When the `kv_unstable_serde` or `kv_unstable_sval` features are enabled, you can also + /// When the `kv_serde` or `kv_sval` features are enabled, you can also /// serialize a value using its `Serialize` or `Value` implementation. pub fn visit(&self, visitor: impl VisitValue<'v>) -> Result<(), Error> { inner::visit(&self.inner, visitor) @@ -230,7 +230,7 @@ impl<'v> fmt::Display for Value<'v> { } } -#[cfg(feature = "kv_unstable_serde")] +#[cfg(feature = "kv_serde")] impl<'v> serde::Serialize for Value<'v> { fn serialize(&self, s: S) -> Result where @@ -240,14 +240,14 @@ impl<'v> serde::Serialize for Value<'v> { } } -#[cfg(feature = "kv_unstable_sval")] +#[cfg(feature = "kv_sval")] impl<'v> sval::Value for Value<'v> { fn stream<'sval, S: sval::Stream<'sval> + ?Sized>(&'sval self, stream: &mut S) -> sval::Result { sval::Value::stream(&self.inner, stream) } } -#[cfg(feature = "kv_unstable_sval")] +#[cfg(feature = "kv_sval")] impl<'v> sval_ref::ValueRef<'v> for Value<'v> { fn stream_ref + ?Sized>(&self, stream: &mut S) -> sval::Result { sval_ref::ValueRef::stream_ref(&self.inner, stream) @@ -362,7 +362,7 @@ impl_value_to_primitive![ impl<'v> Value<'v> { /// Try convert this value into an error. - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] pub fn to_borrowed_error(&self) -> Option<&(dyn std::error::Error + 'static)> { self.inner.to_borrowed_error() } @@ -373,7 +373,7 @@ impl<'v> Value<'v> { } } -#[cfg(feature = "kv_unstable_std")] +#[cfg(feature = "kv_std")] mod std_support { use std::borrow::Cow; use std::rc::Rc; @@ -507,13 +507,13 @@ pub trait VisitValue<'v> { } /// Visit an error. - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_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")] + #[cfg(feature = "kv_std")] fn visit_borrowed_error( &mut self, err: &'v (dyn std::error::Error + 'static), @@ -570,12 +570,12 @@ where (**self).visit_char(value) } - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] fn visit_error(&mut self, err: &(dyn std::error::Error + 'static)) -> Result<(), Error> { (**self).visit_error(err) } - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] fn visit_borrowed_error( &mut self, err: &'v (dyn std::error::Error + 'static), @@ -676,7 +676,7 @@ pub(in crate::kv) mod inner { .map_err(crate::kv::Error::into_value) } - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] fn visit_error( &mut self, err: &(dyn std::error::Error + 'static), @@ -686,7 +686,7 @@ pub(in crate::kv) mod inner { .map_err(crate::kv::Error::into_value) } - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] fn visit_borrowed_error( &mut self, err: &'v (dyn std::error::Error + 'static), @@ -1158,7 +1158,7 @@ pub(crate) mod tests { for v in str() { assert!(v.to_borrowed_str().is_some()); - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] assert!(v.to_cow_str().is_some()); } @@ -1167,13 +1167,13 @@ pub(crate) mod tests { assert!(v.to_borrowed_str().is_some()); - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] assert!(v.to_cow_str().is_some()); for v in unsigned().chain(signed()).chain(float()).chain(bool()) { assert!(v.to_borrowed_str().is_none()); - #[cfg(feature = "kv_unstable_std")] + #[cfg(feature = "kv_std")] assert!(v.to_cow_str().is_none()); } } diff --git a/src/lib.rs b/src/lib.rs index 8abbfcb2e..feb14e115 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,7 +88,7 @@ //! //! ## Structured logging //! -//! If you enable the `kv_unstable` feature you can associate structured values +//! If you enable the `kv` feature you can associate structured values //! with your log records. If we take the example from before, we can include //! some additional context besides what's in the formatted message: //! @@ -97,7 +97,7 @@ //! # #[derive(Debug, Serialize)] pub struct Yak(String); //! # impl Yak { fn shave(&mut self, _: u32) {} } //! # fn find_a_razor() -> Result { Ok(1) } -//! # #[cfg(feature = "kv_unstable_serde")] +//! # #[cfg(feature = "kv_serde")] //! # fn main() { //! use log::{info, warn}; //! @@ -118,7 +118,7 @@ //! } //! } //! # } -//! # #[cfg(not(feature = "kv_unstable_serde"))] +//! # #[cfg(not(feature = "kv_serde"))] //! # fn main() {} //! ``` //! @@ -355,7 +355,7 @@ use std::{cmp, fmt, mem}; mod macros; mod serde; -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] pub mod kv; #[cfg(target_has_atomic = "ptr")] @@ -723,7 +723,7 @@ pub struct Record<'a> { module_path: Option>, file: Option>, line: Option, - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] key_values: KeyValues<'a>, } @@ -731,11 +731,11 @@ pub struct Record<'a> { // `#[derive(Debug)]` on `Record`. It also // provides a useful `Debug` implementation for // the underlying `Source`. -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] #[derive(Clone)] struct KeyValues<'a>(&'a dyn kv::Source); -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] impl<'a> fmt::Debug for KeyValues<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut visitor = f.debug_map(); @@ -812,14 +812,14 @@ impl<'a> Record<'a> { } /// The structured key-value pairs associated with the message. - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] #[inline] pub fn key_values(&self) -> &dyn kv::Source { self.key_values.0 } /// Create a new [`RecordBuilder`](struct.RecordBuilder.html) based on this record. - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] #[inline] pub fn to_builder(&self) -> RecordBuilder { RecordBuilder { @@ -904,7 +904,7 @@ impl<'a> RecordBuilder<'a> { module_path: None, file: None, line: None, - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] key_values: KeyValues(&None::<(kv::Key, kv::Value)>), }, } @@ -974,7 +974,7 @@ impl<'a> RecordBuilder<'a> { } /// Set [`key_values`](struct.Record.html#method.key_values) - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] #[inline] pub fn key_values(&mut self, kvs: &'a dyn kv::Source) -> &mut RecordBuilder<'a> { self.record.key_values = KeyValues(kvs); @@ -1757,7 +1757,7 @@ mod tests { } #[test] - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] fn test_record_key_values_builder() { use super::Record; use crate::kv::{self, VisitSource}; @@ -1788,7 +1788,7 @@ mod tests { } #[test] - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] fn test_record_key_values_get_coerce() { use super::Record; diff --git a/src/macros.rs b/src/macros.rs index 74de7f892..9ad6e7f08 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -231,7 +231,7 @@ macro_rules! log_enabled { #[doc(hidden)] #[macro_export] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] macro_rules! __log_key { // key1 = 42 ($($args:ident)*) => { @@ -245,16 +245,16 @@ macro_rules! __log_key { #[doc(hidden)] #[macro_export] -#[cfg(not(feature = "kv_unstable"))] +#[cfg(not(feature = "kv"))] macro_rules! __log_key { ($($args:tt)*) => { - compile_error!("key value support requires the `kv_unstable` feature of `log`") + compile_error!("key value support requires the `kv` feature of `log`") }; } #[doc(hidden)] #[macro_export] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] macro_rules! __log_value { // Default (($args:expr)) => { @@ -294,25 +294,27 @@ macro_rules! __log_value { #[doc(hidden)] #[macro_export] -#[cfg(not(feature = "kv_unstable"))] +#[cfg(not(feature = "kv"))] macro_rules! __log_value { ($($args:tt)*) => { - compile_error!("key value support requires the `kv_unstable` feature of `log`") + compile_error!("key value support requires the `kv` feature of `log`") }; } #[doc(hidden)] #[macro_export] -#[cfg(not(feature = "kv_unstable_std"))] +#[cfg(not(feature = "kv_std"))] macro_rules! __log_value_error { ($args:expr) => { - compile_error!("capturing values as `std::error::Error` requites the `kv_unstable_std` feature of `log`") - } + compile_error!( + "capturing values as `std::error::Error` requites the `kv_std` feature of `log`" + ) + }; } #[doc(hidden)] #[macro_export] -#[cfg(feature = "kv_unstable_sval")] +#[cfg(feature = "kv_sval")] macro_rules! __log_value_sval { ($args:expr) => { $crate::__private_api::capture_sval(&&$args) @@ -321,18 +323,16 @@ macro_rules! __log_value_sval { #[doc(hidden)] #[macro_export] -#[cfg(not(feature = "kv_unstable_sval"))] +#[cfg(not(feature = "kv_sval"))] macro_rules! __log_value_sval { ($args:expr) => { - compile_error!( - "capturing values as `sval::Value` requites the `kv_unstable_sval` feature of `log`" - ) + compile_error!("capturing values as `sval::Value` requites the `kv_sval` feature of `log`") }; } #[doc(hidden)] #[macro_export] -#[cfg(feature = "kv_unstable_serde")] +#[cfg(feature = "kv_serde")] macro_rules! __log_value_serde { ($args:expr) => { $crate::__private_api::capture_serde(&&$args) @@ -341,16 +341,18 @@ macro_rules! __log_value_serde { #[doc(hidden)] #[macro_export] -#[cfg(not(feature = "kv_unstable_serde"))] +#[cfg(not(feature = "kv_serde"))] macro_rules! __log_value_serde { ($args:expr) => { - compile_error!("capturing values as `serde::Serialize` requites the `kv_unstable_serde` feature of `log`") - } + compile_error!( + "capturing values as `serde::Serialize` requites the `kv_serde` feature of `log`" + ) + }; } #[doc(hidden)] #[macro_export] -#[cfg(feature = "kv_unstable_std")] +#[cfg(feature = "kv_std")] macro_rules! __log_value_error { ($args:expr) => { $crate::__private_api::capture_error(&$args) diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 7d0d3e853..8754bb16a 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -7,10 +7,10 @@ build = "src/build.rs" [features] std = ["log/std"] -kv_unstable = ["log/kv_unstable"] -kv_unstable_std = ["log/kv_unstable_std"] -kv_unstable_sval = ["log/kv_unstable_sval"] -kv_unstable_serde = ["log/kv_unstable_serde"] +kv = ["log/kv"] +kv_std = ["log/kv_std"] +kv_sval = ["log/kv_sval"] +kv_serde = ["log/kv_serde"] [dependencies.log] path = ".." diff --git a/tests/macros.rs b/tests/macros.rs index 47cc957d9..07bdf53f0 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -107,7 +107,7 @@ fn expr() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_no_args() { for lvl in log::Level::iter() { log!(target: "my_target", lvl, cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello"); @@ -121,7 +121,7 @@ fn kv_no_args() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_expr_args() { for lvl in log::Level::iter() { log!(target: "my_target", lvl, cat_math = { let mut x = 0; x += 1; x + 1 }; "hello"); @@ -136,7 +136,7 @@ fn kv_expr_args() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_anonymous_args() { for lvl in log::Level::iter() { log!(target: "my_target", lvl, cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello {}", "world"); @@ -151,7 +151,7 @@ fn kv_anonymous_args() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_named_args() { for lvl in log::Level::iter() { log!(target: "my_target", lvl, cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello {world}", world = "world"); @@ -166,7 +166,7 @@ fn kv_named_args() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_expr_context() { match "chashu" { cat_1 => { @@ -196,14 +196,14 @@ fn implicit_named_args() { all_log_macros!(target: "my_target", "hello {world}"); all_log_macros!(target: "my_target", "hello {world}",); - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] all_log_macros!(target = "my_target"; "hello {world}"); - #[cfg(feature = "kv_unstable")] + #[cfg(feature = "kv")] all_log_macros!(target = "my_target"; "hello {world}",); } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_implicit_named_args() { let world = "world"; @@ -219,7 +219,7 @@ fn kv_implicit_named_args() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_string_keys() { for lvl in log::Level::iter() { log!(target: "my_target", lvl, "also dogs" = "Fílos", "key/that-can't/be/an/ident" = "hi"; "hello {world}", world = "world"); @@ -229,7 +229,7 @@ fn kv_string_keys() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_common_value_types() { all_log_macros!( u8 = 42u8, @@ -251,7 +251,7 @@ fn kv_common_value_types() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_debug() { all_log_macros!( a:? = 42, @@ -261,7 +261,7 @@ fn kv_debug() { } #[test] -#[cfg(feature = "kv_unstable")] +#[cfg(feature = "kv")] fn kv_display() { all_log_macros!( a:% = 42, @@ -271,7 +271,7 @@ fn kv_display() { } #[test] -#[cfg(feature = "kv_unstable_std")] +#[cfg(feature = "kv_std")] fn kv_error() { all_log_macros!( a:error = std::io::Error::new(std::io::ErrorKind::Other, "an error"); @@ -280,7 +280,7 @@ fn kv_error() { } #[test] -#[cfg(feature = "kv_unstable_sval")] +#[cfg(feature = "kv_sval")] fn kv_sval() { all_log_macros!( a:sval = 42; @@ -289,7 +289,7 @@ fn kv_sval() { } #[test] -#[cfg(feature = "kv_unstable_serde")] +#[cfg(feature = "kv_serde")] fn kv_serde() { all_log_macros!( a:serde = 42; From 90a347bd836873264a393a35bfd90fe478fadae2 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 19 Feb 2024 07:22:40 +1000 Subject: [PATCH 20/26] restore removed APIs as deprecated --- Cargo.toml | 3 +- src/kv/mod.rs | 11 ++++ src/kv/source.rs | 4 ++ src/kv/value.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 5992d0a9e..3074e9cfa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,8 @@ kv_sval = ["kv", "value-bag/sval", "sval", "sval_ref"] kv_std = ["std", "kv", "value-bag/error"] kv_serde = ["kv_std", "value-bag/serde", "serde"] -# Legacy: use `kv_*` instead +# Deprecated: use `kv_*` instead +# These `*_unstable` features will be removed in a future release kv_unstable = ["kv"] kv_unstable_sval = ["kv_sval"] kv_unstable_std = ["kv_std"] diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 44f7b06df..6dbfe6f66 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -236,10 +236,21 @@ mod error; mod key; + +#[cfg(not(feature = "kv_unstable"))] mod source; +#[cfg(not(feature = "kv_unstable"))] mod value; pub use self::error::Error; pub use self::key::{Key, ToKey}; pub use self::source::{Source, VisitSource}; pub use self::value::{ToValue, Value, VisitValue}; + +#[cfg(feature = "kv_unstable")] +pub mod source; +#[cfg(feature = "kv_unstable")] +pub mod value; + +#[cfg(feature = "kv_unstable")] +pub use self::source::Visitor; diff --git a/src/kv/source.rs b/src/kv/source.rs index 33d5a1a73..0ca267ce3 100644 --- a/src/kv/source.rs +++ b/src/kv/source.rs @@ -458,6 +458,10 @@ mod std_support { } } +// NOTE: Deprecated; but aliases can't carry this attribute +#[cfg(feature = "kv_unstable")] +pub use VisitSource as Visitor; + #[cfg(test)] mod tests { use crate::kv::value; diff --git a/src/kv/value.rs b/src/kv/value.rs index 6bfff4e4e..96cb411d1 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -298,6 +298,12 @@ macro_rules! impl_to_value_primitive { Value::from_inner(value) } } + + impl<'v> From<&'v $into_ty> for Value<'v> { + fn from(value: &'v $into_ty) -> Self { + Value::from_inner(*value) + } + } )* }; } @@ -316,6 +322,12 @@ macro_rules! impl_to_value_nonzero_primitive { Value::from(value.get()) } } + + impl<'v> From<&'v std::num::$into_ty> for Value<'v> { + fn from(value: &'v std::num::$into_ty) -> Self { + Value::from(value.get()) + } + } )* }; } @@ -1034,6 +1046,130 @@ pub(in crate::kv) mod inner { } } +impl<'v> Value<'v> { + /// Get a value from a type implementing `std::fmt::Debug`. + #[cfg(feature = "kv_unstable")] + #[deprecated(note = "use `from_debug` instead")] + pub fn capture_debug(value: &'v T) -> Self + where + T: fmt::Debug + 'static, + { + Value::from_debug(value) + } + + /// Get a value from a type implementing `std::fmt::Display`. + #[cfg(feature = "kv_unstable")] + #[deprecated(note = "use `from_display` instead")] + pub fn capture_display(value: &'v T) -> Self + where + T: fmt::Display + 'static, + { + Value::from_display(value) + } + + /// Get a value from an error. + #[cfg(feature = "kv_unstable_std")] + #[deprecated(note = "use `from_dyn_error` instead")] + pub fn capture_error(err: &'v T) -> Self + where + T: std::error::Error + 'static, + { + Value::from_dyn_error(err) + } + + /// Get a value from a type implementing `serde::Serialize`. + #[cfg(feature = "kv_unstable_serde")] + #[deprecated(note = "use `from_serde` instead")] + pub fn capture_serde(value: &'v T) -> Self + where + T: serde::Serialize + 'static, + { + Value::from_serde(value) + } + + /// Get a value from a type implementing `sval::Value`. + #[cfg(feature = "kv_unstable_sval")] + #[deprecated(note = "use `from_sval` instead")] + pub fn capture_sval(value: &'v T) -> Self + where + T: sval::Value + 'static, + { + Value::from_sval(value) + } + + /// Check whether this value can be downcast to `T`. + #[cfg(feature = "kv_unstable")] + #[deprecated( + note = "downcasting has been removed; log an issue at https://github.com/rust-lang/log/issues if this is something you rely on" + )] + pub fn is(&self) -> bool { + false + } + + /// Try downcast this value to `T`. + #[cfg(feature = "kv_unstable")] + #[deprecated( + note = "downcasting has been removed; log an issue at https://github.com/rust-lang/log/issues if this is something you rely on" + )] + pub fn downcast_ref(&self) -> Option<&T> { + None + } +} + +// NOTE: Deprecated; but aliases can't carry this attribute +#[cfg(feature = "kv_unstable")] +pub use VisitValue as Visitor; + +/// Get a value from a type implementing `std::fmt::Debug`. +#[cfg(feature = "kv_unstable")] +#[deprecated(note = "use the `key:? = value` macro syntax instead")] +#[macro_export] +macro_rules! as_debug { + ($capture:expr) => { + $crate::kv::Value::from_debug(&$capture) + }; +} + +/// Get a value from a type implementing `std::fmt::Display`. +#[cfg(feature = "kv_unstable")] +#[deprecated(note = "use the `key:% = value` macro syntax instead")] +#[macro_export] +macro_rules! as_display { + ($capture:expr) => { + $crate::kv::Value::from_display(&$capture) + }; +} + +/// Get a value from an error. +#[cfg(feature = "kv_unstable_std")] +#[deprecated(note = "use the `key:error = value` macro syntax instead")] +#[macro_export] +macro_rules! as_error { + ($capture:expr) => { + $crate::kv::Value::from_dyn_error(&$capture) + }; +} + +#[cfg(feature = "kv_unstable_serde")] +#[deprecated(note = "use the `key:serde = value` macro syntax instead")] +/// Get a value from a type implementing `serde::Serialize`. +#[macro_export] +macro_rules! as_serde { + ($capture:expr) => { + $crate::kv::Value::from_serde(&$capture) + }; +} + +/// Get a value from a type implementing `sval::Value`. +#[cfg(feature = "kv_unstable_sval")] +#[deprecated(note = "use the `key:sval = value` macro syntax instead")] +#[macro_export] +macro_rules! as_sval { + ($capture:expr) => { + $crate::kv::Value::from_sval(&$capture) + }; +} + #[cfg(test)] pub(crate) mod tests { use super::*; From ad917118a5e781d0dd60b3a75ba519ce9839ba70 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 19 Feb 2024 07:36:01 +1000 Subject: [PATCH 21/26] support field shorthand in macros --- README.md | 2 +- src/kv/mod.rs | 11 ++++++++++- src/kv/value.rs | 2 +- src/lib.rs | 2 +- src/macros.rs | 17 +++++++++++++---- tests/macros.rs | 11 ++++++++++- 6 files changed, 36 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 01fd30875..c959ac56d 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ pub fn shave_the_yak(yak: &mut Yak) { break; } Err(err) => { - warn!(err:error = err; "Unable to locate a razor, retrying"); + warn!(err:err; "Unable to locate a razor, retrying"); } } } diff --git a/src/kv/mod.rs b/src/kv/mod.rs index 6dbfe6f66..1ccb82514 100644 --- a/src/kv/mod.rs +++ b/src/kv/mod.rs @@ -29,6 +29,15 @@ //! info!(a = 1; "Something of interest"); //! ``` //! +//! Key-values support the same shorthand identifer syntax as `format_args`: +//! +//! ``` +//! # use log::info; +//! let a = 1; +//! +//! info!(a; "Something of interest"); +//! ``` +//! //! Values are capturing using the [`ToValue`] trait by default. To capture a value //! using a different trait implementation, use a modifier after its key. Here's how //! the same example can capture `a` using its `Debug` implementation instead: @@ -44,7 +53,7 @@ //! - `:debug` will capture the value using `Debug`. //! - `:%` will capture the value using `Display`. //! - `:display` will capture the value using `Display`. -//! - `:error` will capture the value using `std::error::Error` (requires the `kv_std` feature). +//! - `:err` will capture the value using `std::error::Error` (requires the `kv_std` feature). //! - `:sval` will capture the value using `sval::Value` (requires the `kv_sval` feature). //! - `:serde` will capture the value using `serde::Serialize` (requires the `kv_serde` feature). //! diff --git a/src/kv/value.rs b/src/kv/value.rs index 96cb411d1..c7efa279d 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -1142,7 +1142,7 @@ macro_rules! as_display { /// Get a value from an error. #[cfg(feature = "kv_unstable_std")] -#[deprecated(note = "use the `key:error = value` macro syntax instead")] +#[deprecated(note = "use the `key:err = value` macro syntax instead")] #[macro_export] macro_rules! as_error { ($capture:expr) => { diff --git a/src/lib.rs b/src/lib.rs index feb14e115..c1f88fe8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -112,7 +112,7 @@ //! break; //! } //! Err(err) => { -//! warn!(err:error = err; "Unable to locate a razor, retrying"); +//! warn!(err:err; "Unable to locate a razor, retrying"); //! } //! } //! } diff --git a/src/macros.rs b/src/macros.rs index 9ad6e7f08..ccccd5bd6 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -30,7 +30,7 @@ #[macro_export] macro_rules! log { // log!(target: "my_target", Level::Info, key1:? = 42, key2 = true; "a {} event", "log"); - (target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? = $value:expr),+; $($arg:tt)+) => ({ + (target: $target:expr, $lvl:expr, $($key:tt $(:$capture:tt)? $(= $value:expr)?),+; $($arg:tt)+) => ({ let lvl = $lvl; if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { $crate::__private_api::log::<&_>( @@ -38,7 +38,7 @@ macro_rules! log { lvl, &($target, $crate::__private_api::module_path!(), $crate::__private_api::file!()), $crate::__private_api::line!(), - &[$(($crate::__log_key!($key), $crate::__log_value!(($value)$(:$capture)*))),+] + &[$(($crate::__log_key!($key), $crate::__log_value!($key $(:$capture)* = $($value)*))),+] ); } }); @@ -256,10 +256,19 @@ macro_rules! __log_key { #[macro_export] #[cfg(feature = "kv")] macro_rules! __log_value { - // Default - (($args:expr)) => { + // Entrypoint + ($key:tt = $args:expr) => { $crate::__log_value!(($args):value) }; + ($key:tt :$capture:tt = $args:expr) => { + $crate::__log_value!(($args):$capture) + }; + ($key:ident =) => { + $crate::__log_value!(($key):value) + }; + ($key:ident :$capture:tt =) => { + $crate::__log_value!(($key):$capture) + }; // ToValue (($args:expr):value) => { $crate::__private_api::capture_to_value(&&$args) diff --git a/tests/macros.rs b/tests/macros.rs index 07bdf53f0..20da6ac44 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -165,6 +165,15 @@ fn kv_named_args() { all_log_macros!(cat_1 = "chashu", cat_2 = "nori", cat_count = 2; "hello {world}", world = "world"); } +#[test] +#[cfg(feature = "kv")] +fn kv_ident() { + let cat_1 = "chashu"; + let cat_2 = "nori"; + + all_log_macros!(cat_1, cat_2:%, cat_count = 2; "hello {world}", world = "world"); +} + #[test] #[cfg(feature = "kv")] fn kv_expr_context() { @@ -274,7 +283,7 @@ fn kv_display() { #[cfg(feature = "kv_std")] fn kv_error() { all_log_macros!( - a:error = std::io::Error::new(std::io::ErrorKind::Other, "an error"); + a:err = std::io::Error::new(std::io::ErrorKind::Other, "an error"); "hello world" ); } From 31bb4b0ff36e458c6bef304a336b71f6342ddcc7 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 19 Feb 2024 07:41:02 +1000 Subject: [PATCH 22/26] move error macros together --- src/macros.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index ccccd5bd6..6297eb853 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -310,17 +310,6 @@ macro_rules! __log_value { }; } -#[doc(hidden)] -#[macro_export] -#[cfg(not(feature = "kv_std"))] -macro_rules! __log_value_error { - ($args:expr) => { - compile_error!( - "capturing values as `std::error::Error` requites the `kv_std` feature of `log`" - ) - }; -} - #[doc(hidden)] #[macro_export] #[cfg(feature = "kv_sval")] @@ -367,3 +356,14 @@ macro_rules! __log_value_error { $crate::__private_api::capture_error(&$args) }; } + +#[doc(hidden)] +#[macro_export] +#[cfg(not(feature = "kv_std"))] +macro_rules! __log_value_error { + ($args:expr) => { + compile_error!( + "capturing values as `std::error::Error` requites the `kv_std` feature of `log`" + ) + }; +} From 73e953905b970ef765a86bf6cbd69bc2c5e2bac4 Mon Sep 17 00:00:00 2001 From: KodrAus Date: Mon, 19 Feb 2024 07:47:32 +1000 Subject: [PATCH 23/26] fix up capturing of :err --- src/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/macros.rs b/src/macros.rs index 6297eb853..48a7447ef 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -288,7 +288,7 @@ macro_rules! __log_value { $crate::__private_api::capture_display(&&$args) }; //Error - (($args:expr):error) => { + (($args:expr):err) => { $crate::__log_value_error!($args) }; // sval::Value From cf85c38d3519745d60e7b891c4b2025050a8389f Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 20 Feb 2024 07:29:14 +1000 Subject: [PATCH 24/26] add needed subfeatures to kv_unstable Co-authored-by: Thomas de Zeeuw --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3074e9cfa..cf64b43e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,10 +53,10 @@ kv_serde = ["kv_std", "value-bag/serde", "serde"] # Deprecated: use `kv_*` instead # These `*_unstable` features will be removed in a future release -kv_unstable = ["kv"] -kv_unstable_sval = ["kv_sval"] -kv_unstable_std = ["kv_std"] -kv_unstable_serde = ["kv_serde"] +kv_unstable = ["kv", "value-bag"] +kv_unstable_sval = ["kv_sval", "kv_unstable"] +kv_unstable_std = ["kv_std", "kv_unstable"] +kv_unstable_serde = ["kv_serde", "kv_unstable_std"] [dependencies] serde = { version = "1.0", optional = true, default-features = false } From 646e9ab9917fb79e44b6b36b8375106a1a09766c Mon Sep 17 00:00:00 2001 From: Ashley Mannix Date: Tue, 20 Feb 2024 07:29:43 +1000 Subject: [PATCH 25/26] use original Visitor name for VisitValue Co-authored-by: Thomas de Zeeuw --- src/kv/value.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/kv/value.rs b/src/kv/value.rs index c7efa279d..1511dd02e 100644 --- a/src/kv/value.rs +++ b/src/kv/value.rs @@ -1118,7 +1118,7 @@ impl<'v> Value<'v> { // NOTE: Deprecated; but aliases can't carry this attribute #[cfg(feature = "kv_unstable")] -pub use VisitValue as Visitor; +pub use VisitValue as Visit; /// Get a value from a type implementing `std::fmt::Debug`. #[cfg(feature = "kv_unstable")] From 2b220bf3b705f2abc0ee591c7eb17972a979da3a Mon Sep 17 00:00:00 2001 From: KodrAus Date: Tue, 20 Feb 2024 07:36:07 +1000 Subject: [PATCH 26/26] clean up structured logging example --- README.md | 13 +++++++++---- src/lib.rs | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c959ac56d..d6a08f558 100644 --- a/README.md +++ b/README.md @@ -106,17 +106,22 @@ If you enable the `kv` feature, you can associate structured data with your log use log::{info, trace, warn}; pub fn shave_the_yak(yak: &mut Yak) { - trace!(target = "yak_events", yak:serde = yak; "Commencing yak shaving"); + // `yak:serde` will capture `yak` using its `serde::Serialize` impl + // + // You could also use `:?` for `Debug`, or `:%` for `Display`. For a + // full list, see the `log` crate documentation + trace!(target = "yak_events", yak:serde; "Commencing yak shaving"); loop { match find_a_razor() { Ok(razor) => { - info!(razor = razor; "Razor located"); + info!(razor; "Razor located"); yak.shave(razor); break; } - Err(err) => { - warn!(err:err; "Unable to locate a razor, retrying"); + Err(e) => { + // `e:err` will capture `e` using its `std::error::Error` impl + warn!(e:err; "Unable to locate a razor, retrying"); } } } diff --git a/src/lib.rs b/src/lib.rs index c1f88fe8d..505d24961 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,17 +102,17 @@ //! use log::{info, warn}; //! //! pub fn shave_the_yak(yak: &mut Yak) { -//! info!(target: "yak_events", yak:serde = yak; "Commencing yak shaving"); +//! info!(target: "yak_events", yak:serde; "Commencing yak shaving"); //! //! loop { //! match find_a_razor() { //! Ok(razor) => { -//! info!(razor = razor; "Razor located"); +//! info!(razor; "Razor located"); //! yak.shave(razor); //! break; //! } -//! Err(err) => { -//! warn!(err:err; "Unable to locate a razor, retrying"); +//! Err(e) => { +//! warn!(e:err; "Unable to locate a razor, retrying"); //! } //! } //! }