From 421a1aa20bd51756186d4df8800267376525b085 Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sun, 13 Aug 2023 15:57:07 +0800 Subject: [PATCH] Specialize empty key value pairs --- src/__private_api.rs | 120 ++++++++++++++++++++++++++++++++----------- src/macros.rs | 6 +-- 2 files changed, 93 insertions(+), 33 deletions(-) diff --git a/src/__private_api.rs b/src/__private_api.rs index 7304deb89..a7110688d 100644 --- a/src/__private_api.rs +++ b/src/__private_api.rs @@ -1,57 +1,117 @@ //! WARNING: this is not part of the crate's public API and is subject to change at any time +use self::sealed::KVs; use crate::{Level, Metadata, Record}; use std::fmt::Arguments; -pub use std::option::Option; 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 fn log( +pub type Value<'a> = str; + +mod sealed { + /// Argument that provides key-value pairs. + pub trait KVs<'a> { + fn into_kvs(self) -> &'a [(&'a str, &'a super::Value<'a>)]; + } +} + +// key values. + +impl<'a> KVs<'a> for &'a [(&'a str, &'a Value<'a>)] { + #[inline] + fn into_kvs(self) -> &'a [(&'a str, &'a Value<'a>)] { + self + } +} + +impl<'a> KVs<'a> for () { + #[inline] + fn into_kvs(self) -> &'a [(&'a str, &'a Value<'a>)] { + &[] + } +} + +// Log implementation. + +fn log_impl( args: Arguments, level: Level, &(target, module_path, file): &(&str, &'static str, &'static str), line: u32, - kvs: Option<&[(&str, &str)]>, + kvs: &[(&str, &Value)], ) { - if kvs.is_some() { + #[cfg(not(feature = "kv_unstable"))] + if !kvs.is_empty() { panic!( "key-value support is experimental and must be enabled using the `kv_unstable` feature" ) } - crate::logger().log( - &Record::builder() - .args(args) - .level(level) - .target(target) - .module_path_static(Some(module_path)) - .file_static(Some(file)) - .line(Some(line)) - .build(), - ); + let mut builder = Record::builder(); + + builder + .args(args) + .level(level) + .target(target) + .module_path_static(Some(module_path)) + .file_static(Some(file)) + .line(Some(line)); + + #[cfg(feature = "kv_unstable")] + builder.key_values(&kvs); + + crate::logger().log(&builder.build()); } -#[cfg(feature = "kv_unstable")] -pub fn log( +pub fn log<'a, K>( args: Arguments, level: Level, - &(target, module_path, file): &(&str, &'static str, &'static str), + target_module_path_and_file: &(&'a str, &'static str, &'static str), line: u32, - kvs: Option<&[(&str, &dyn crate::kv::ToValue)]>, -) { - crate::logger().log( - &Record::builder() - .args(args) - .level(level) - .target(target) - .module_path_static(Some(module_path)) - .file_static(Some(file)) - .line(Some(line)) - .key_values(&kvs) - .build(), - ); + kvs: K, +) where + K: KVs<'a>, +{ + log_impl( + args, + level, + target_module_path_and_file, + line, + kvs.into_kvs(), + ) } pub fn enabled(level: Level, target: &str) -> bool { crate::logger().enabled(&Metadata::builder().level(level).target(target).build()) } + +/// This function is not intended to be actually called by anyone. The intention of this function is for forcing the +/// compiler to generate monomorphizations of the `log` function, so that they can be shared by different downstream +/// crates. +/// +/// The idea is that with [`share-generics`](https://github.com/rust-lang/rust/pull/48779), downstream crates can reuse +/// generic monomorphizations from upstream crates, but not siblings crates. So it is best to instantiate these +/// monomorphizations in the `log` crate, so downstream crates are guaranteed to be able to share these +/// monomorphizations. +pub fn instantiate_log_function() -> usize { + struct State(usize); + + impl State { + fn add<'a, K>(&mut self) + where + K: KVs<'a>, + { + self.0 ^= log:: as usize; + } + } + + let mut state = State(0); + + state.add::<&[(&str, &Value)]>(); + state.add::<()>(); + + state.0 +} diff --git a/src/macros.rs b/src/macros.rs index 281ff2572..30d7c10a0 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -33,12 +33,12 @@ macro_rules! log { (target: $target:expr, $lvl:expr, $($key:tt = $value:expr),+; $($arg:tt)+) => ({ let lvl = $lvl; if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() { - $crate::__private_api::log( + $crate::__private_api::log::<&_>( $crate::__private_api::format_args!($($arg)+), lvl, &($target, $crate::__private_api::module_path!(), $crate::__private_api::file!()), $crate::__private_api::line!(), - $crate::__private_api::Option::Some(&[$(($crate::__log_key!($key), &$value)),+]) + &[$(($crate::__log_key!($key), &$value)),+] ); } }); @@ -52,7 +52,7 @@ macro_rules! log { lvl, &($target, $crate::__private_api::module_path!(), $crate::__private_api::file!()), $crate::__private_api::line!(), - $crate::__private_api::Option::None, + (), ); } });