Upgrade rust/crates/log to 0.4.14
Test: make
Change-Id: I5abe243d26889b36f56963e9a9d22185dd5b9558
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index f8f6640..798cd7f 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
{
"git": {
- "sha1": "aa4c0375cd94ed4d1b5de7ce0bb3369262dae7e2"
+ "sha1": "9d4206770dd93f07cb27c3e1f41dc21c45031302"
}
}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index ed53c52..9a8f9aa 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -40,8 +40,9 @@
- 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 std"
- - run: cargo test --verbose --features "kv_unstable_sval"
+ - 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 run --verbose --manifest-path test_max_level_features/Cargo.toml
- run: cargo run --verbose --manifest-path test_max_level_features/Cargo.toml --release
@@ -57,6 +58,22 @@
rustup component add rustfmt
- run: cargo fmt -- --check
+ features:
+ name: Feature check
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@master
+ - name: Install Rust
+ 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"
+
msrv:
name: MSRV
runs-on: ubuntu-latest
@@ -69,6 +86,7 @@
- run: cargo build --verbose
- run: cargo build --verbose --features serde
- run: cargo build --verbose --features std
+ - run: cargo test --verbose --manifest-path tests/Cargo.toml
embedded:
name: Embedded
@@ -79,5 +97,6 @@
run: |
rustup update stable --no-self-update
rustup default stable
- - run: rustup target add thumbv6m-none-eabi
+ - run: rustup target add thumbv6m-none-eabi riscv32imc-unknown-none-elf
- run: cargo build --verbose --target=thumbv6m-none-eabi
+ - run: cargo build --verbose --target=riscv32imc-unknown-none-elf
diff --git a/Android.bp b/Android.bp
index 048de29..0b57b3c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,5 @@
// This file is generated by cargo2android.py --run --device --dependencies --features=std --patch=patches/Android.bp.diff.
+// Do not modify this file as changes will be overridden on upgrade.
package {
default_applicable_licenses: ["external_rust_crates_log_license"],
@@ -46,6 +47,7 @@
features: ["std"],
flags: [
"--cfg atomic_cas",
+ "--cfg has_atomics",
],
rustlibs: [
"libcfg_if",
@@ -58,4 +60,4 @@
}
// dependent_library ["feature_list"]
-// cfg-if-0.1.10
+// cfg-if-1.0.0
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 296b49b..63c3ccd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,32 @@
## [Unreleased]
+## [0.4.14] - 2021-01-27
+
+* Remove the `__private_api_log_lit` special case.
+* Fixed incorrect combination of `kv_unstable` and `std` features causing compile failures.
+* Remove unstable `Value::to_*` conversions that were incorrectly using `as`.
+* Rename unstable `Value::to_error` to `Value::to_borrowed_error`.
+
+## [0.4.13] - 2021-01-11
+
+* This is the same as `0.4.11`, except with a `kv_unstable_std` feature added to aid migrating current dependents to `0.4.14` (which was originally going to be `0.4.13` until it was decided to create a patch from `0.4.11` to minimize disruption).
+
+## [0.4.12] - 2020-12-24
+
+### New
+
+* Support platforms without atomics by racing instead of failing to compile
+* Implement `Log` for `Box<T: Log>`
+* Update `cfg-if` to `1.0`
+* Internal reworks of the structured logging API. Removed the `Fill` API
+and added `source::as_map` and `source::as_list` to easily serialize a `Source`
+as either a map of `{key: value, ..}` or as a list of `[(key, value), ..]`.
+
+### Fixed
+
+* Fixed deserialization of `LevelFilter` to use their `u64` index variants
+
## [0.4.11] - 2020-07-09
### New
@@ -170,7 +196,10 @@
Look at the [release tags] for information about older releases.
-[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.11...HEAD
+[Unreleased]: https://github.com/rust-lang-nursery/log/compare/0.4.14...HEAD
+[0.4.14]: https://github.com/rust-lang-nursery/log/compare/0.4.13...0.4.14
+[0.4.13]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.13
+[0.4.12]: https://github.com/rust-lang-nursery/log/compare/0.4.11...0.4.12
[0.4.11]: https://github.com/rust-lang-nursery/log/compare/0.4.10...0.4.11
[0.4.10]: https://github.com/rust-lang-nursery/log/compare/0.4.9...0.4.10
[0.4.9]: https://github.com/rust-lang-nursery/log/compare/0.4.8...0.4.9
diff --git a/Cargo.toml b/Cargo.toml
index bd654d2..3cbe5e0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@
[package]
name = "log"
-version = "0.4.11"
+version = "0.4.14"
authors = ["The Rust Project Developers"]
build = "build.rs"
exclude = ["rfcs/**/*", "/.travis.yml", "/appveyor.yml"]
@@ -24,17 +24,19 @@
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/log"
[package.metadata.docs.rs]
-features = ["std", "serde", "kv_unstable_sval"]
+features = ["std", "serde", "kv_unstable_std", "kv_unstable_sval", "kv_unstable_serde"]
[[test]]
name = "filters"
+path = "tests/filters.rs"
harness = false
[[test]]
name = "macros"
+path = "tests/macros.rs"
harness = true
[dependencies.cfg-if]
-version = "0.1.2"
+version = "1.0"
[dependencies.serde]
version = "1.0"
@@ -42,19 +44,34 @@
default-features = false
[dependencies.sval]
-version = "0.5.2"
+version = "1.0.0-alpha.5"
optional = true
default-features = false
+
+[dependencies.value-bag]
+version = "1.0.0-alpha.6"
+optional = true
+default-features = false
+[dev-dependencies.serde]
+version = "1.0"
+features = ["derive"]
+
[dev-dependencies.serde_test]
version = "1.0"
[dev-dependencies.sval]
-version = "0.5.2"
+version = "1.0.0-alpha.5"
+features = ["derive"]
+
+[dev-dependencies.value-bag]
+version = "1.0.0-alpha.6"
features = ["test"]
[features]
-kv_unstable = []
-kv_unstable_sval = ["kv_unstable", "sval/fmt"]
+kv_unstable = ["value-bag"]
+kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"]
+kv_unstable_std = ["std", "kv_unstable", "value-bag/error"]
+kv_unstable_sval = ["kv_unstable", "value-bag/sval", "sval"]
max_level_debug = []
max_level_error = []
max_level_info = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 447469e..0d31859 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,7 +1,7 @@
[package]
name = "log"
-version = "0.4.11" # remember to update html_root_url
+version = "0.4.14" # remember to update html_root_url
authors = ["The Rust Project Developers"]
license = "MIT OR Apache-2.0"
readme = "README.md"
@@ -16,14 +16,16 @@
build = "build.rs"
[package.metadata.docs.rs]
-features = ["std", "serde", "kv_unstable_sval"]
+features = ["std", "serde", "kv_unstable_std", "kv_unstable_sval", "kv_unstable_serde"]
[[test]]
name = "filters"
+path = "tests/filters.rs"
harness = false
[[test]]
name = "macros"
+path = "tests/macros.rs"
harness = true
[features]
@@ -45,14 +47,19 @@
# requires the latest stable
# this will have a tighter MSRV before stabilization
-kv_unstable = []
-kv_unstable_sval = ["kv_unstable", "sval/fmt"]
+kv_unstable = ["value-bag"]
+kv_unstable_sval = ["kv_unstable", "value-bag/sval", "sval"]
+kv_unstable_std = ["std", "kv_unstable", "value-bag/error"]
+kv_unstable_serde = ["kv_unstable_std", "value-bag/serde", "serde"]
[dependencies]
-cfg-if = "0.1.2"
+cfg-if = "1.0"
serde = { version = "1.0", optional = true, default-features = false }
-sval = { version = "0.5.2", optional = true, default-features = false }
+sval = { version = "1.0.0-alpha.5", optional = true, default-features = false }
+value-bag = { version = "1.0.0-alpha.6", optional = true, default-features = false }
[dev-dependencies]
+serde = { version = "1.0", features = ["derive"] }
serde_test = "1.0"
-sval = { version = "0.5.2", features = ["test"] }
+sval = { version = "1.0.0-alpha.5", features = ["derive"] }
+value-bag = { version = "1.0.0-alpha.6", features = ["test"] }
diff --git a/METADATA b/METADATA
index 281092f..86f9298 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/log/log-0.4.11.crate"
+ value: "https://static.crates.io/crates/log/log-0.4.14.crate"
}
- version: "0.4.11"
+ version: "0.4.14"
license_type: NOTICE
last_upgrade_date {
- year: 2020
- month: 7
- day: 15
+ year: 2021
+ month: 4
+ day: 2
}
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 32a5803..1b7b681 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,14 +1,56 @@
-// Generated by cargo2android.py for tests in Android.bp
+// Generated by update_crate_tests.py for tests that depend on this crate.
{
"presubmit": [
{
- "name": "env_logger_device_test_src_lib"
+ "name": "env_logger_device_test_tests_regexp_filter"
+ },
+ {
+ "name": "env_logger_device_test_tests_log-in-log"
+ },
+ {
+ "name": "tokio-test_device_test_tests_macros"
+ },
+ {
+ "name": "keystore2_test"
+ },
+ {
+ "name": "vpnprofilestore_test"
+ },
+ {
+ "name": "tokio-test_device_test_src_lib"
+ },
+ {
+ "name": "libsqlite3-sys_device_test_src_lib"
+ },
+ {
+ "name": "keystore2_selinux_test"
},
{
"name": "android_logger_device_test_src_lib"
},
{
- "name": "libsqlite3-sys_device_test_src_lib"
+ "name": "env_logger_device_test_tests_log_tls_dtors"
+ },
+ {
+ "name": "tokio-test_device_test_tests_block_on"
+ },
+ {
+ "name": "futures-util_device_test_src_lib"
+ },
+ {
+ "name": "tokio-test_device_test_tests_io"
+ },
+ {
+ "name": "keystore2_crypto_test_rust"
+ },
+ {
+ "name": "env_logger_device_test_src_lib"
+ },
+ {
+ "name": "quiche_device_test_src_lib"
+ },
+ {
+ "name": "env_logger_device_test_tests_init-twice-retains-filter"
}
]
}
diff --git a/benches/value.rs b/benches/value.rs
new file mode 100644
index 0000000..3dab3bf
--- /dev/null
+++ b/benches/value.rs
@@ -0,0 +1,30 @@
+#![cfg(feature = "kv_unstable")]
+#![feature(test)]
+
+extern crate log;
+extern crate test;
+
+use log::kv::Value;
+
+#[bench]
+fn u8_to_value(b: &mut test::Bencher) {
+ b.iter(|| Value::from(1u8))
+}
+
+#[bench]
+fn u8_to_value_debug(b: &mut test::Bencher) {
+ b.iter(|| Value::from_debug(&1u8))
+}
+
+#[bench]
+fn str_to_value_debug(b: &mut test::Bencher) {
+ b.iter(|| Value::from_debug(&"a string"))
+}
+
+#[bench]
+fn custom_to_value_debug(b: &mut test::Bencher) {
+ #[derive(Debug)]
+ struct A;
+
+ b.iter(|| Value::from_debug(&A))
+}
diff --git a/build.rs b/build.rs
index 65d2398..22cfa78 100644
--- a/build.rs
+++ b/build.rs
@@ -2,13 +2,42 @@
//! atomics and sets `cfg` flags accordingly.
use std::env;
+use std::str;
fn main() {
- let target = env::var("TARGET").unwrap();
+ let target = match rustc_target() {
+ Some(target) => target,
+ None => return,
+ };
- if !target.starts_with("thumbv6") {
+ if target_has_atomic_cas(&target) {
println!("cargo:rustc-cfg=atomic_cas");
}
+ if target_has_atomics(&target) {
+ println!("cargo:rustc-cfg=has_atomics");
+ }
+
println!("cargo:rerun-if-changed=build.rs");
}
+
+fn target_has_atomic_cas(target: &str) -> bool {
+ match &target[..] {
+ "thumbv6m-none-eabi"
+ | "msp430-none-elf"
+ | "riscv32i-unknown-none-elf"
+ | "riscv32imc-unknown-none-elf" => false,
+ _ => true,
+ }
+}
+
+fn target_has_atomics(target: &str) -> bool {
+ match &target[..] {
+ "msp430-none-elf" | "riscv32i-unknown-none-elf" | "riscv32imc-unknown-none-elf" => false,
+ _ => true,
+ }
+}
+
+fn rustc_target() -> Option<String> {
+ env::var("TARGET").ok()
+}
diff --git a/patches/Android.bp.diff b/patches/Android.bp.diff
index f3cc736..f0de6f5 100644
--- a/patches/Android.bp.diff
+++ b/patches/Android.bp.diff
@@ -1,6 +1,6 @@
--- Android.bp 2020-12-14 20:08:02.451621093 +0900
+++ Android.bp.new 2020-12-14 20:05:44.460062409 +0900
-@@ -14,6 +14,11 @@ rust_library {
+@@ -50,6 +50,11 @@ rust_library {
rustlibs: [
"libcfg_if",
],
diff --git a/src/kv/key.rs b/src/kv/key.rs
index 367f6ed..0e688c1 100644
--- a/src/kv/key.rs
+++ b/src/kv/key.rs
@@ -128,6 +128,39 @@
}
}
+#[cfg(feature = "kv_unstable_sval")]
+mod sval_support {
+ use super::*;
+
+ extern crate sval;
+
+ use self::sval::value::{self, Value};
+
+ impl<'a> Value for Key<'a> {
+ fn stream(&self, stream: &mut value::Stream) -> value::Result {
+ self.key.stream(stream)
+ }
+ }
+}
+
+#[cfg(feature = "kv_unstable_serde")]
+mod serde_support {
+ use super::*;
+
+ extern crate serde;
+
+ use self::serde::{Serialize, Serializer};
+
+ impl<'a> Serialize for Key<'a> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.key.serialize(serializer)
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/kv/mod.rs b/src/kv/mod.rs
index f4c0866..f96b821 100644
--- a/src/kv/mod.rs
+++ b/src/kv/mod.rs
@@ -14,7 +14,7 @@
mod error;
mod key;
-mod source;
+pub mod source;
pub mod value;
diff --git a/src/kv/source.rs b/src/kv/source.rs
index eeac1ec..e3ecde9 100644
--- a/src/kv/source.rs
+++ b/src/kv/source.rs
@@ -1,5 +1,11 @@
//! Sources for key-value pairs.
+#[cfg(feature = "kv_unstable_sval")]
+extern crate sval;
+
+#[cfg(feature = "kv_unstable_serde")]
+extern crate serde;
+
use kv::{Error, Key, ToKey, ToValue, Value};
use std::fmt;
@@ -30,28 +36,14 @@
///
/// A source that can provide a more efficient implementation of this method
/// should override it.
+ #[cfg(not(test))]
fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
- struct Get<'k, 'v> {
- key: Key<'k>,
- found: Option<Value<'v>>,
- }
-
- impl<'k, 'kvs> Visitor<'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);
- }
-
- Ok(())
- }
- }
-
- let mut get = Get { key, found: None };
-
- let _ = self.visit(&mut get);
- get.found
+ get_default(self, key)
}
+ #[cfg(test)]
+ fn get<'v>(&'v self, key: Key) -> Option<Value<'v>>;
+
/// Count the number of key-value pairs that can be visited.
///
/// # Implementation notes
@@ -61,21 +53,53 @@
///
/// A subsequent call to `visit` should yield the same number of key-value pairs
/// to the visitor, unless that visitor fails part way through.
+ #[cfg(not(test))]
fn count(&self) -> usize {
- struct Count(usize);
-
- impl<'kvs> Visitor<'kvs> for Count {
- fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> {
- self.0 += 1;
-
- Ok(())
- }
- }
-
- let mut count = Count(0);
- let _ = self.visit(&mut count);
- count.0
+ count_default(self)
}
+
+ #[cfg(test)]
+ fn count(&self) -> usize;
+}
+
+/// The default implemention of `Source::get`
+pub(crate) fn get_default<'v>(source: &'v (impl Source + ?Sized), key: Key) -> Option<Value<'v>> {
+ struct Get<'k, 'v> {
+ key: Key<'k>,
+ found: Option<Value<'v>>,
+ }
+
+ impl<'k, 'kvs> Visitor<'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);
+ }
+
+ Ok(())
+ }
+ }
+
+ let mut get = Get { key, found: None };
+
+ let _ = source.visit(&mut get);
+ get.found
+}
+
+/// The default implementation of `Source::count`.
+pub(crate) fn count_default(source: impl Source) -> usize {
+ struct Count(usize);
+
+ impl<'kvs> Visitor<'kvs> for Count {
+ fn visit_pair(&mut self, _: Key<'kvs>, _: Value<'kvs>) -> Result<(), Error> {
+ self.0 += 1;
+
+ Ok(())
+ }
+ }
+
+ let mut count = Count(0);
+ let _ = source.visit(&mut count);
+ count.0
}
impl<'a, T> Source for &'a T
@@ -129,6 +153,16 @@
Ok(())
}
+ fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
+ for source in self {
+ if let Some(found) = source.get(key.clone()) {
+ return Some(found);
+ }
+ }
+
+ None
+ }
+
fn count(&self) -> usize {
self.len()
}
@@ -146,6 +180,10 @@
Ok(())
}
+ fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
+ self.as_ref().and_then(|s| s.get(key))
+ }
+
fn count(&self) -> usize {
self.as_ref().map(Source::count).unwrap_or(0)
}
@@ -291,7 +329,7 @@
#[cfg(test)]
mod tests {
use super::*;
- use kv::value::test::Token;
+ use kv::value::tests::Token;
use std::collections::{BTreeMap, HashMap};
#[test]
@@ -340,10 +378,313 @@
}
}
+/// The result of calling `Source::as_map`.
+pub struct AsMap<S>(S);
+
+/// Visit this source as a map.
+pub fn as_map<S>(source: S) -> AsMap<S>
+where
+ S: Source,
+{
+ AsMap(source)
+}
+
+impl<S> Source for AsMap<S>
+where
+ S: Source,
+{
+ fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> {
+ self.0.visit(visitor)
+ }
+
+ fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
+ self.0.get(key)
+ }
+
+ fn count(&self) -> usize {
+ self.0.count()
+ }
+}
+
+impl<S> fmt::Debug for AsMap<S>
+where
+ S: Source,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut f = f.debug_map();
+ self.0.visit(&mut f).map_err(|_| fmt::Error)?;
+ f.finish()
+ }
+}
+
+/// The result of calling `Source::as_list`
+pub struct AsList<S>(S);
+
+/// Visit this source as a list.
+pub fn as_list<S>(source: S) -> AsList<S>
+where
+ S: Source,
+{
+ AsList(source)
+}
+
+impl<S> Source for AsList<S>
+where
+ S: Source,
+{
+ fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> {
+ self.0.visit(visitor)
+ }
+
+ fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
+ self.0.get(key)
+ }
+
+ fn count(&self) -> usize {
+ self.0.count()
+ }
+}
+
+impl<S> fmt::Debug for AsList<S>
+where
+ S: Source,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut f = f.debug_list();
+ self.0.visit(&mut f).map_err(|_| fmt::Error)?;
+ f.finish()
+ }
+}
+
+#[cfg(feature = "kv_unstable_sval")]
+mod sval_support {
+ use super::*;
+
+ use self::sval::value;
+
+ impl<S> value::Value for AsMap<S>
+ where
+ S: Source,
+ {
+ fn stream(&self, stream: &mut value::Stream) -> value::Result {
+ struct StreamVisitor<'a, 'b>(&'a mut value::Stream<'b>);
+
+ impl<'a, 'b, 'kvs> Visitor<'kvs> for StreamVisitor<'a, 'b> {
+ fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> {
+ self.0
+ .map_key(key)
+ .map_err(|_| Error::msg("failed to stream map key"))?;
+ self.0
+ .map_value(value)
+ .map_err(|_| Error::msg("failed to stream map value"))?;
+ Ok(())
+ }
+ }
+
+ stream
+ .map_begin(Some(self.count()))
+ .map_err(|_| self::sval::Error::msg("failed to begin map"))?;
+
+ self.visit(&mut StreamVisitor(stream))
+ .map_err(|_| self::sval::Error::msg("failed to visit key-values"))?;
+
+ stream
+ .map_end()
+ .map_err(|_| self::sval::Error::msg("failed to end map"))
+ }
+ }
+
+ impl<S> value::Value for AsList<S>
+ where
+ S: Source,
+ {
+ fn stream(&self, stream: &mut value::Stream) -> value::Result {
+ struct StreamVisitor<'a, 'b>(&'a mut value::Stream<'b>);
+
+ impl<'a, 'b, 'kvs> Visitor<'kvs> for StreamVisitor<'a, 'b> {
+ fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> {
+ self.0
+ .seq_elem((key, value))
+ .map_err(|_| Error::msg("failed to stream seq entry"))?;
+ Ok(())
+ }
+ }
+
+ stream
+ .seq_begin(Some(self.count()))
+ .map_err(|_| self::sval::Error::msg("failed to begin seq"))?;
+
+ self.visit(&mut StreamVisitor(stream))
+ .map_err(|_| self::sval::Error::msg("failed to visit key-values"))?;
+
+ stream
+ .seq_end()
+ .map_err(|_| self::sval::Error::msg("failed to end seq"))
+ }
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use super::*;
+
+ use self::sval::Value;
+
+ use crate::kv::source;
+
+ #[test]
+ fn derive_stream() {
+ #[derive(Value)]
+ pub struct MyRecordAsMap<'a> {
+ msg: &'a str,
+ kvs: source::AsMap<&'a dyn Source>,
+ }
+
+ #[derive(Value)]
+ pub struct MyRecordAsList<'a> {
+ msg: &'a str,
+ kvs: source::AsList<&'a dyn Source>,
+ }
+ }
+ }
+}
+
+#[cfg(feature = "kv_unstable_serde")]
+pub mod as_map {
+ //! `serde` adapters for serializing a `Source` as a map.
+
+ use super::*;
+
+ use self::serde::{Serialize, Serializer};
+
+ /// Serialize a `Source` as a map.
+ pub fn serialize<T, S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ T: Source,
+ S: Serializer,
+ {
+ as_map(source).serialize(serializer)
+ }
+}
+
+#[cfg(feature = "kv_unstable_serde")]
+pub mod as_list {
+ //! `serde` adapters for serializing a `Source` as a list.
+
+ use super::*;
+
+ use self::serde::{Serialize, Serializer};
+
+ /// Serialize a `Source` as a list.
+ pub fn serialize<T, S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ T: Source,
+ S: Serializer,
+ {
+ as_list(source).serialize(serializer)
+ }
+}
+
+#[cfg(feature = "kv_unstable_serde")]
+mod serde_support {
+ use super::*;
+
+ use self::serde::ser::{Error as SerError, Serialize, SerializeMap, SerializeSeq, Serializer};
+
+ impl<T> Serialize for AsMap<T>
+ where
+ T: Source,
+ {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ struct SerializerVisitor<'a, S>(&'a mut S);
+
+ impl<'a, 'kvs, S> Visitor<'kvs> for SerializerVisitor<'a, S>
+ where
+ S: SerializeMap,
+ {
+ fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> {
+ self.0
+ .serialize_entry(&key, &value)
+ .map_err(|_| Error::msg("failed to serialize map entry"))?;
+ Ok(())
+ }
+ }
+
+ let mut map = serializer.serialize_map(Some(self.count()))?;
+
+ self.visit(&mut SerializerVisitor(&mut map))
+ .map_err(|_| S::Error::custom("failed to visit key-values"))?;
+
+ map.end()
+ }
+ }
+
+ impl<T> Serialize for AsList<T>
+ where
+ T: Source,
+ {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ struct SerializerVisitor<'a, S>(&'a mut S);
+
+ impl<'a, 'kvs, S> Visitor<'kvs> for SerializerVisitor<'a, S>
+ where
+ S: SerializeSeq,
+ {
+ fn visit_pair(&mut self, key: Key<'kvs>, value: Value<'kvs>) -> Result<(), Error> {
+ self.0
+ .serialize_element(&(key, value))
+ .map_err(|_| Error::msg("failed to serialize seq entry"))?;
+ Ok(())
+ }
+ }
+
+ let mut seq = serializer.serialize_seq(Some(self.count()))?;
+
+ self.visit(&mut SerializerVisitor(&mut seq))
+ .map_err(|_| S::Error::custom("failed to visit seq"))?;
+
+ seq.end()
+ }
+ }
+
+ #[cfg(test)]
+ mod tests {
+ use super::*;
+
+ use self::serde::Serialize;
+
+ use crate::kv::source;
+
+ #[test]
+ fn derive_serialize() {
+ #[derive(Serialize)]
+ pub struct MyRecordAsMap<'a> {
+ msg: &'a str,
+ #[serde(flatten)]
+ #[serde(with = "source::as_map")]
+ kvs: &'a dyn Source,
+ }
+
+ #[derive(Serialize)]
+ pub struct MyRecordAsList<'a> {
+ msg: &'a str,
+ #[serde(flatten)]
+ #[serde(with = "source::as_list")]
+ kvs: &'a dyn Source,
+ }
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
- use kv::value::test::Token;
+ use kv::value::tests::Token;
#[test]
fn source_is_object_safe() {
@@ -366,6 +707,14 @@
fn visit<'kvs>(&'kvs self, visitor: &mut dyn Visitor<'kvs>) -> Result<(), Error> {
visitor.visit_pair(self.key.to_key(), self.value.to_value())
}
+
+ fn get<'v>(&'v self, key: Key) -> Option<Value<'v>> {
+ get_default(self, key)
+ }
+
+ fn count(&self) -> usize {
+ count_default(self)
+ }
}
assert_eq!(1, Source::count(&("a", 1)));
@@ -390,4 +739,16 @@
let source = Option::None::<(&str, i32)>;
assert!(Source::get(&source, Key::from_str("a")).is_none());
}
+
+ #[test]
+ fn as_map() {
+ let _ = crate::kv::source::as_map(("a", 1));
+ let _ = crate::kv::source::as_map(&("a", 1) as &dyn Source);
+ }
+
+ #[test]
+ fn as_list() {
+ let _ = crate::kv::source::as_list(("a", 1));
+ let _ = crate::kv::source::as_list(&("a", 1) as &dyn Source);
+ }
}
diff --git a/src/kv/value.rs b/src/kv/value.rs
new file mode 100644
index 0000000..942d59e
--- /dev/null
+++ b/src/kv/value.rs
@@ -0,0 +1,655 @@
+//! Structured values.
+
+use std::fmt;
+
+extern crate value_bag;
+
+#[cfg(feature = "kv_unstable_sval")]
+extern crate sval;
+
+#[cfg(feature = "kv_unstable_serde")]
+extern crate serde;
+
+use self::value_bag::ValueBag;
+
+pub use kv::Error;
+
+/// A type that can be converted into a [`Value`](struct.Value.html).
+pub trait ToValue {
+ /// Perform the conversion.
+ fn to_value(&self) -> Value;
+}
+
+impl<'a, T> ToValue for &'a T
+where
+ T: ToValue + ?Sized,
+{
+ fn to_value(&self) -> Value {
+ (**self).to_value()
+ }
+}
+
+impl<'v> ToValue for Value<'v> {
+ fn to_value(&self) -> Value {
+ Value {
+ inner: self.inner.clone(),
+ }
+ }
+}
+
+/// A value in a structured key-value pair.
+///
+/// # 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.
+/// These methods don't require `T: 'static`, but can't support downcasting.
+///
+/// ```
+/// use log::kv::Value;
+///
+/// let value = Value::from_debug(&42i32);
+///
+/// assert_eq!(None, value.to_i64());
+/// ```
+///
+/// ## Using the `ToValue` trait
+///
+/// The `ToValue` trait can be used to capture values generically.
+/// It's the bound used by `Source`.
+///
+/// ```
+/// # use log::kv::ToValue;
+/// let value = 42i32.to_value();
+///
+/// assert_eq!(Some(42), value.to_i64());
+/// ```
+///
+/// ```
+/// # use std::fmt::Debug;
+/// use log::kv::ToValue;
+///
+/// let value = (&42i32 as &dyn Debug).to_value();
+///
+/// assert_eq!(None, value.to_i64());
+/// ```
+///
+/// ## Using the standard `From` trait
+///
+/// Standard types that implement `ToValue` also implement `From`.
+///
+/// ```
+/// use log::kv::Value;
+///
+/// let value = Value::from(42i32);
+///
+/// assert_eq!(Some(42), value.to_i64());
+/// ```
+pub struct Value<'v> {
+ inner: ValueBag<'v>,
+}
+
+impl<'v> Value<'v> {
+ /// Get a value from a type implementing `ToValue`.
+ pub fn from_any<T>(value: &'v T) -> Self
+ where
+ T: ToValue,
+ {
+ value.to_value()
+ }
+
+ /// Get a value from a type implementing `std::fmt::Debug`.
+ pub fn capture_debug<T>(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<T>(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<T>(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<T>(value: &'v T) -> Self
+ where
+ T: self::serde::Serialize + 'static,
+ {
+ Value {
+ inner: ValueBag::capture_serde1(value),
+ }
+ }
+
+ /// Get a value from a type implementing `sval::value::Value`.
+ #[cfg(feature = "kv_unstable_sval")]
+ pub fn capture_sval<T>(value: &'v T) -> Self
+ where
+ T: self::sval::value::Value + 'static,
+ {
+ Value {
+ inner: ValueBag::capture_sval1(value),
+ }
+ }
+
+ /// Get a value from a type implementing `std::fmt::Debug`.
+ pub fn from_debug<T>(value: &'v T) -> Self
+ where
+ T: fmt::Debug,
+ {
+ Value {
+ inner: ValueBag::from_debug(value),
+ }
+ }
+
+ /// Get a value from a type implementing `std::fmt::Display`.
+ pub fn from_display<T>(value: &'v T) -> Self
+ where
+ T: fmt::Display,
+ {
+ Value {
+ inner: ValueBag::from_display(value),
+ }
+ }
+
+ /// Get a value from a type implementing `serde::Serialize`.
+ #[cfg(feature = "kv_unstable_serde")]
+ pub fn from_serde<T>(value: &'v T) -> Self
+ where
+ T: self::serde::Serialize,
+ {
+ Value {
+ inner: ValueBag::from_serde1(value),
+ }
+ }
+
+ /// Get a value from a type implementing `sval::value::Value`.
+ #[cfg(feature = "kv_unstable_sval")]
+ pub fn from_sval<T>(value: &'v T) -> Self
+ where
+ T: self::sval::value::Value,
+ {
+ Value {
+ inner: ValueBag::from_sval1(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),
+ }
+ }
+
+ /// 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),
+ }
+ }
+
+ /// Get a value from a dynamic error.
+ #[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),
+ }
+ }
+
+ /// Get a value from a type implementing `sval::value::Value`.
+ #[cfg(feature = "kv_unstable_sval")]
+ pub fn from_dyn_sval(value: &'v dyn self::sval::value::Value) -> Self {
+ Value {
+ inner: ValueBag::from_dyn_sval1(value),
+ }
+ }
+
+ /// Get a value from an internal primitive.
+ fn from_value_bag<T>(value: T) -> Self
+ where
+ T: Into<ValueBag<'v>>,
+ {
+ Value {
+ inner: value.into(),
+ }
+ }
+
+ /// Check whether this value can be downcast to `T`.
+ pub fn is<T: 'static>(&self) -> bool {
+ self.inner.is::<T>()
+ }
+
+ /// Try downcast this value to `T`.
+ pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
+ self.inner.downcast_ref::<T>()
+ }
+}
+
+impl<'v> fmt::Debug for Value<'v> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(&self.inner, f)
+ }
+}
+
+impl<'v> fmt::Display for Value<'v> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.inner, f)
+ }
+}
+
+impl ToValue for dyn fmt::Debug {
+ fn to_value(&self) -> Value {
+ Value::from_dyn_debug(self)
+ }
+}
+
+impl ToValue for dyn fmt::Display {
+ fn to_value(&self) -> Value {
+ Value::from_dyn_display(self)
+ }
+}
+
+#[cfg(feature = "kv_unstable_std")]
+impl ToValue for dyn std::error::Error + 'static {
+ fn to_value(&self) -> Value {
+ Value::from_dyn_error(self)
+ }
+}
+
+#[cfg(feature = "kv_unstable_serde")]
+impl<'v> self::serde::Serialize for Value<'v> {
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where
+ S: self::serde::Serializer,
+ {
+ self.inner.serialize(s)
+ }
+}
+
+#[cfg(feature = "kv_unstable_sval")]
+impl<'v> self::sval::value::Value for Value<'v> {
+ fn stream(&self, stream: &mut self::sval::value::Stream) -> self::sval::value::Result {
+ self::sval::value::Value::stream(&self.inner, stream)
+ }
+}
+
+#[cfg(feature = "kv_unstable_sval")]
+impl ToValue for dyn self::sval::value::Value {
+ fn to_value(&self) -> Value {
+ Value::from_dyn_sval(self)
+ }
+}
+
+impl ToValue for str {
+ fn to_value(&self) -> Value {
+ Value::from(self)
+ }
+}
+
+impl<'v> From<&'v str> for Value<'v> {
+ fn from(value: &'v str) -> Self {
+ Value::from_value_bag(value)
+ }
+}
+
+impl ToValue for () {
+ fn to_value(&self) -> Value {
+ Value::from_value_bag(())
+ }
+}
+
+impl<T> ToValue for Option<T>
+where
+ T: ToValue,
+{
+ fn to_value(&self) -> Value {
+ match *self {
+ Some(ref value) => value.to_value(),
+ None => Value::from_value_bag(()),
+ }
+ }
+}
+
+macro_rules! impl_to_value_primitive {
+ ($($into_ty:ty,)*) => {
+ $(
+ impl ToValue for $into_ty {
+ fn to_value(&self) -> Value {
+ Value::from(*self)
+ }
+ }
+
+ impl<'v> From<$into_ty> for Value<'v> {
+ fn from(value: $into_ty) -> Self {
+ Value::from_value_bag(value)
+ }
+ }
+ )*
+ };
+}
+
+macro_rules! impl_value_to_primitive {
+ ($(#[doc = $doc:tt] $into_name:ident -> $into_ty:ty,)*) => {
+ impl<'v> Value<'v> {
+ $(
+ #[doc = $doc]
+ pub fn $into_name(&self) -> Option<$into_ty> {
+ self.inner.$into_name()
+ }
+ )*
+ }
+ }
+}
+
+impl_to_value_primitive![
+ usize, u8, u16, u32, u64, u128, isize, i8, i16, i32, i64, i128, f32, f64, char, bool,
+];
+
+impl_value_to_primitive![
+ #[doc = "Try convert this value into a `u64`."]
+ to_u64 -> u64,
+ #[doc = "Try convert this value into a `i64`."]
+ to_i64 -> i64,
+ #[doc = "Try convert this value into a `u128`."]
+ to_u128 -> u128,
+ #[doc = "Try convert this value into a `i128`."]
+ to_i128 -> i128,
+ #[doc = "Try convert this value into a `f64`."]
+ to_f64 -> f64,
+ #[doc = "Try convert this value into a `char`."]
+ to_char -> char,
+ #[doc = "Try convert this value into a `bool`."]
+ to_bool -> bool,
+];
+
+impl<'v> Value<'v> {
+ /// Try convert this value into an error.
+ #[cfg(feature = "kv_unstable_std")]
+ pub fn to_borrowed_error(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.inner.to_borrowed_error()
+ }
+
+ /// Try convert this value into a borrowed string.
+ pub fn to_borrowed_str(&self) -> Option<&str> {
+ self.inner.to_borrowed_str()
+ }
+}
+
+#[cfg(feature = "kv_unstable_std")]
+mod std_support {
+ use super::*;
+
+ use std::borrow::Cow;
+
+ impl<T> ToValue for Box<T>
+ where
+ T: ToValue + ?Sized,
+ {
+ fn to_value(&self) -> Value {
+ (**self).to_value()
+ }
+ }
+
+ impl ToValue for String {
+ fn to_value(&self) -> Value {
+ Value::from(&**self)
+ }
+ }
+
+ impl<'v> ToValue for Cow<'v, str> {
+ fn to_value(&self) -> Value {
+ Value::from(&**self)
+ }
+ }
+
+ impl<'v> Value<'v> {
+ /// Try convert this value into a string.
+ pub fn to_str(&self) -> Option<Cow<str>> {
+ self.inner.to_str()
+ }
+ }
+}
+
+#[cfg(test)]
+pub(crate) mod tests {
+ use super::*;
+
+ pub(crate) use super::value_bag::test::Token;
+
+ impl<'v> Value<'v> {
+ pub(crate) fn to_token(&self) -> Token {
+ self.inner.to_token()
+ }
+ }
+
+ fn unsigned() -> impl Iterator<Item = Value<'static>> {
+ vec![
+ Value::from(8u8),
+ Value::from(16u16),
+ Value::from(32u32),
+ Value::from(64u64),
+ Value::from(1usize),
+ ]
+ .into_iter()
+ }
+
+ fn signed() -> impl Iterator<Item = Value<'static>> {
+ vec![
+ Value::from(-8i8),
+ Value::from(-16i16),
+ Value::from(-32i32),
+ Value::from(-64i64),
+ Value::from(-1isize),
+ ]
+ .into_iter()
+ }
+
+ fn float() -> impl Iterator<Item = Value<'static>> {
+ vec![Value::from(32.32f32), Value::from(64.64f64)].into_iter()
+ }
+
+ fn bool() -> impl Iterator<Item = Value<'static>> {
+ vec![Value::from(true), Value::from(false)].into_iter()
+ }
+
+ fn str() -> impl Iterator<Item = Value<'static>> {
+ vec![Value::from("a string"), Value::from("a loong string")].into_iter()
+ }
+
+ fn char() -> impl Iterator<Item = Value<'static>> {
+ 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");
+ assert_eq!(42i64.to_value().to_string(), "42");
+ assert_eq!(42.01f64.to_value().to_string(), "42.01");
+ assert_eq!(true.to_value().to_string(), "true");
+ assert_eq!('a'.to_value().to_string(), "a");
+ assert_eq!("a loong string".to_value().to_string(), "a loong string");
+ assert_eq!(Some(true).to_value().to_string(), "true");
+ assert_eq!(().to_value().to_string(), "None");
+ assert_eq!(Option::None::<bool>.to_value().to_string(), "None");
+ }
+
+ #[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!(
+ "a loong string".to_value().to_token(),
+ 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!(Option::None::<bool>.to_value().to_token(), Token::None);
+ }
+
+ #[test]
+ fn test_to_number() {
+ for v in unsigned() {
+ assert!(v.to_u64().is_some());
+ assert!(v.to_i64().is_some());
+ }
+
+ for v in signed() {
+ assert!(v.to_i64().is_some());
+ }
+
+ for v in unsigned().chain(signed()).chain(float()) {
+ assert!(v.to_f64().is_some());
+ }
+
+ for v in bool().chain(str()).chain(char()) {
+ assert!(v.to_u64().is_none());
+ assert!(v.to_i64().is_none());
+ assert!(v.to_f64().is_none());
+ }
+ }
+
+ #[test]
+ fn test_to_str() {
+ for v in str() {
+ assert!(v.to_borrowed_str().is_some());
+
+ #[cfg(feature = "kv_unstable_std")]
+ assert!(v.to_str().is_some());
+ }
+
+ let short_lived = String::from("short lived");
+ let v = Value::from(&*short_lived);
+
+ assert!(v.to_borrowed_str().is_some());
+
+ #[cfg(feature = "kv_unstable_std")]
+ assert!(v.to_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")]
+ assert!(v.to_str().is_none());
+ }
+ }
+
+ #[test]
+ fn test_to_bool() {
+ for v in bool() {
+ assert!(v.to_bool().is_some());
+ }
+
+ for v in unsigned()
+ .chain(signed())
+ .chain(float())
+ .chain(str())
+ .chain(char())
+ {
+ assert!(v.to_bool().is_none());
+ }
+ }
+
+ #[test]
+ fn test_to_char() {
+ for v in char() {
+ assert!(v.to_char().is_some());
+ }
+
+ for v in unsigned()
+ .chain(signed())
+ .chain(float())
+ .chain(str())
+ .chain(bool())
+ {
+ assert!(v.to_char().is_none());
+ }
+ }
+
+ #[test]
+ fn test_downcast_ref() {
+ #[derive(Debug)]
+ struct Foo(u64);
+
+ let v = Value::capture_debug(&Foo(42));
+
+ assert!(v.is::<Foo>());
+ assert_eq!(42u64, v.downcast_ref::<Foo>().expect("invalid downcast").0);
+ }
+}
diff --git a/src/kv/value/fill.rs b/src/kv/value/fill.rs
deleted file mode 100644
index 2e74899..0000000
--- a/src/kv/value/fill.rs
+++ /dev/null
@@ -1,164 +0,0 @@
-//! Lazy value initialization.
-
-use std::fmt;
-
-use super::internal::{Erased, Inner, Visitor};
-use super::{Error, Value};
-
-impl<'v> Value<'v> {
- /// Get a value from a fillable slot.
- pub fn from_fill<T>(value: &'v T) -> Self
- where
- T: Fill + 'static,
- {
- Value {
- inner: Inner::Fill(unsafe { Erased::new_unchecked::<T>(value) }),
- }
- }
-}
-
-/// A type that requires extra work to convert into a [`Value`](struct.Value.html).
-///
-/// This trait is a more advanced initialization API than [`ToValue`](trait.ToValue.html).
-/// It's intended for erased values coming from other logging frameworks that may need
-/// to perform extra work to determine the concrete type to use.
-pub trait Fill {
- /// Fill a value.
- fn fill(&self, slot: &mut Slot) -> Result<(), Error>;
-}
-
-impl<'a, T> Fill for &'a T
-where
- T: Fill + ?Sized,
-{
- fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
- (**self).fill(slot)
- }
-}
-
-/// A value slot to fill using the [`Fill`](trait.Fill.html) trait.
-pub struct Slot<'s, 'f> {
- filled: bool,
- visitor: &'s mut dyn Visitor<'f>,
-}
-
-impl<'s, 'f> fmt::Debug for Slot<'s, 'f> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.debug_struct("Slot").finish()
- }
-}
-
-impl<'s, 'f> Slot<'s, 'f> {
- pub(super) fn new(visitor: &'s mut dyn Visitor<'f>) -> Self {
- Slot {
- visitor,
- filled: false,
- }
- }
-
- pub(super) fn fill<F>(&mut self, f: F) -> Result<(), Error>
- where
- F: FnOnce(&mut dyn Visitor<'f>) -> Result<(), Error>,
- {
- assert!(!self.filled, "the slot has already been filled");
- self.filled = true;
-
- f(self.visitor)
- }
-
- /// Fill the slot with a value.
- ///
- /// The given value doesn't need to satisfy any particular lifetime constraints.
- ///
- /// # Panics
- ///
- /// Calling more than a single `fill` method on this slot will panic.
- pub fn fill_any<T>(&mut self, value: T) -> Result<(), Error>
- where
- T: Into<Value<'f>>,
- {
- self.fill(|visitor| value.into().inner.visit(visitor))
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn fill_value_borrowed() {
- struct TestFill;
-
- impl Fill for TestFill {
- fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
- let dbg: &dyn fmt::Debug = &1;
-
- slot.fill_debug(&dbg)
- }
- }
-
- assert_eq!("1", Value::from_fill(&TestFill).to_string());
- }
-
- #[test]
- fn fill_value_owned() {
- struct TestFill;
-
- impl Fill for TestFill {
- fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
- slot.fill_any("a string")
- }
- }
- }
-
- #[test]
- #[should_panic]
- fn fill_multiple_times_panics() {
- struct BadFill;
-
- impl Fill for BadFill {
- fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
- slot.fill_any(42)?;
- slot.fill_any(6789)?;
-
- Ok(())
- }
- }
-
- let _ = Value::from_fill(&BadFill).to_string();
- }
-
- #[test]
- fn fill_cast() {
- struct TestFill;
-
- impl Fill for TestFill {
- fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
- slot.fill_any("a string")
- }
- }
-
- assert_eq!(
- "a string",
- Value::from_fill(&TestFill)
- .to_borrowed_str()
- .expect("invalid value")
- );
- }
-
- #[test]
- fn fill_debug() {
- struct TestFill;
-
- impl Fill for TestFill {
- fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
- slot.fill_any(42u64)
- }
- }
-
- assert_eq!(
- format!("{:04?}", 42u64),
- format!("{:04?}", Value::from_fill(&TestFill)),
- )
- }
-}
diff --git a/src/kv/value/impls.rs b/src/kv/value/impls.rs
deleted file mode 100644
index 8752dbe..0000000
--- a/src/kv/value/impls.rs
+++ /dev/null
@@ -1,159 +0,0 @@
-//! Converting standard types into `Value`s.
-//!
-//! This module provides `ToValue` implementations for commonly
-//! logged types from the standard library.
-
-use std::fmt;
-
-use super::{Primitive, ToValue, Value};
-
-macro_rules! impl_into_owned {
- ($($into_ty:ty => $convert:ident,)*) => {
- $(
- impl ToValue for $into_ty {
- fn to_value(&self) -> Value {
- Value::from(*self)
- }
- }
-
- impl<'v> From<$into_ty> for Value<'v> {
- fn from(value: $into_ty) -> Self {
- Value::from_primitive(value as $convert)
- }
- }
- )*
- };
-}
-
-impl<'v> ToValue for &'v str {
- fn to_value(&self) -> Value {
- Value::from(*self)
- }
-}
-
-impl<'v> From<&'v str> for Value<'v> {
- fn from(value: &'v str) -> Self {
- Value::from_primitive(value)
- }
-}
-
-impl<'v> ToValue for fmt::Arguments<'v> {
- fn to_value(&self) -> Value {
- Value::from(*self)
- }
-}
-
-impl<'v> From<fmt::Arguments<'v>> for Value<'v> {
- fn from(value: fmt::Arguments<'v>) -> Self {
- Value::from_primitive(value)
- }
-}
-
-impl ToValue for () {
- fn to_value(&self) -> Value {
- Value::from_primitive(Primitive::None)
- }
-}
-
-impl<T> ToValue for Option<T>
-where
- T: ToValue,
-{
- fn to_value(&self) -> Value {
- match *self {
- Some(ref value) => value.to_value(),
- None => Value::from_primitive(Primitive::None),
- }
- }
-}
-
-impl_into_owned! [
- usize => u64,
- u8 => u64,
- u16 => u64,
- u32 => u64,
- u64 => u64,
-
- isize => i64,
- i8 => i64,
- i16 => i64,
- i32 => i64,
- i64 => i64,
-
- f32 => f64,
- f64 => f64,
-
- char => char,
- bool => bool,
-];
-
-#[cfg(feature = "std")]
-mod std_support {
- use super::*;
-
- use std::borrow::Cow;
-
- impl<T> ToValue for Box<T>
- where
- T: ToValue + ?Sized,
- {
- fn to_value(&self) -> Value {
- (**self).to_value()
- }
- }
-
- impl ToValue for String {
- fn to_value(&self) -> Value {
- Value::from_primitive(Primitive::Str(&*self))
- }
- }
-
- impl<'v> ToValue for Cow<'v, str> {
- fn to_value(&self) -> Value {
- Value::from_primitive(Primitive::Str(&*self))
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use kv::value::test::Token;
-
- #[test]
- fn test_to_value_display() {
- assert_eq!(42u64.to_value().to_string(), "42");
- assert_eq!(42i64.to_value().to_string(), "42");
- assert_eq!(42.01f64.to_value().to_string(), "42.01");
- assert_eq!(true.to_value().to_string(), "true");
- assert_eq!('a'.to_value().to_string(), "a");
- assert_eq!(
- format_args!("a {}", "value").to_value().to_string(),
- "a value"
- );
- assert_eq!("a loong string".to_value().to_string(), "a loong string");
- assert_eq!(Some(true).to_value().to_string(), "true");
- assert_eq!(().to_value().to_string(), "None");
- assert_eq!(Option::None::<bool>.to_value().to_string(), "None");
- }
-
- #[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!(
- format_args!("a {}", "value").to_value().to_token(),
- Token::Str("a value".into())
- );
- assert_eq!(
- "a loong string".to_value().to_token(),
- 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!(Option::None::<bool>.to_value().to_token(), Token::None);
- }
-}
diff --git a/src/kv/value/internal/cast.rs b/src/kv/value/internal/cast.rs
deleted file mode 100644
index aaa4b07..0000000
--- a/src/kv/value/internal/cast.rs
+++ /dev/null
@@ -1,475 +0,0 @@
-//! Coerce a `Value` into some concrete types.
-//!
-//! These operations are cheap when the captured value is a simple primitive,
-//! but may end up executing arbitrary caller code if the value is complex.
-//! They will also attempt to downcast erased types into a primitive where possible.
-
-use std::any::TypeId;
-use std::fmt;
-
-use super::{Erased, Inner, Primitive, Visitor};
-use crate::kv::value::{Error, Value};
-
-impl<'v> Value<'v> {
- /// Try get a `usize` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_usize(&self) -> Option<usize> {
- self.inner
- .cast()
- .into_primitive()
- .into_u64()
- .map(|v| v as usize)
- }
-
- /// Try get a `u8` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_u8(&self) -> Option<u8> {
- self.inner
- .cast()
- .into_primitive()
- .into_u64()
- .map(|v| v as u8)
- }
-
- /// Try get a `u16` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_u16(&self) -> Option<u16> {
- self.inner
- .cast()
- .into_primitive()
- .into_u64()
- .map(|v| v as u16)
- }
-
- /// Try get a `u32` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_u32(&self) -> Option<u32> {
- self.inner
- .cast()
- .into_primitive()
- .into_u64()
- .map(|v| v as u32)
- }
-
- /// Try get a `u64` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_u64(&self) -> Option<u64> {
- self.inner.cast().into_primitive().into_u64()
- }
-
- /// Try get a `isize` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_isize(&self) -> Option<isize> {
- self.inner
- .cast()
- .into_primitive()
- .into_i64()
- .map(|v| v as isize)
- }
-
- /// Try get a `i8` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_i8(&self) -> Option<i8> {
- self.inner
- .cast()
- .into_primitive()
- .into_i64()
- .map(|v| v as i8)
- }
-
- /// Try get a `i16` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_i16(&self) -> Option<i16> {
- self.inner
- .cast()
- .into_primitive()
- .into_i64()
- .map(|v| v as i16)
- }
-
- /// Try get a `i32` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_i32(&self) -> Option<i32> {
- self.inner
- .cast()
- .into_primitive()
- .into_i64()
- .map(|v| v as i32)
- }
-
- /// Try get a `i64` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_i64(&self) -> Option<i64> {
- self.inner.cast().into_primitive().into_i64()
- }
-
- /// Try get a `f32` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_f32(&self) -> Option<f32> {
- self.inner
- .cast()
- .into_primitive()
- .into_f64()
- .map(|v| v as f32)
- }
-
- /// Try get a `f64` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_f64(&self) -> Option<f64> {
- self.inner.cast().into_primitive().into_f64()
- }
-
- /// Try get a `bool` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_bool(&self) -> Option<bool> {
- self.inner.cast().into_primitive().into_bool()
- }
-
- /// Try get a `char` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones.
- pub fn to_char(&self) -> Option<char> {
- self.inner.cast().into_primitive().into_char()
- }
-
- /// Try get a `str` from this value.
- ///
- /// This method is cheap for primitive types. It won't allocate an owned
- /// `String` if the value is a complex type.
- pub fn to_borrowed_str(&self) -> Option<&str> {
- self.inner.cast().into_primitive().into_borrowed_str()
- }
-}
-
-impl<'v> Inner<'v> {
- /// Cast the inner value to another type.
- fn cast(self) -> Cast<'v> {
- struct CastVisitor<'v>(Cast<'v>);
-
- impl<'v> Visitor<'v> for CastVisitor<'v> {
- fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> {
- Ok(())
- }
-
- fn u64(&mut self, v: u64) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::Unsigned(v));
- Ok(())
- }
-
- fn i64(&mut self, v: i64) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::Signed(v));
- Ok(())
- }
-
- fn f64(&mut self, v: f64) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::Float(v));
- Ok(())
- }
-
- fn bool(&mut self, v: bool) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::Bool(v));
- Ok(())
- }
-
- fn char(&mut self, v: char) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::Char(v));
- Ok(())
- }
-
- fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::Str(v));
- Ok(())
- }
-
- #[cfg(not(feature = "std"))]
- fn str(&mut self, _: &str) -> Result<(), Error> {
- Ok(())
- }
-
- #[cfg(feature = "std")]
- fn str(&mut self, v: &str) -> Result<(), Error> {
- self.0 = Cast::String(v.into());
- Ok(())
- }
-
- fn none(&mut self) -> Result<(), Error> {
- self.0 = Cast::Primitive(Primitive::None);
- Ok(())
- }
-
- #[cfg(feature = "kv_unstable_sval")]
- fn sval(&mut self, v: &dyn super::sval::Value) -> Result<(), Error> {
- self.0 = super::sval::cast(v);
- Ok(())
- }
- }
-
- // Try downcast an erased value first
- // It also lets us avoid the Visitor infrastructure for simple primitives
- let primitive = match self {
- Inner::Primitive(value) => Some(value),
- Inner::Fill(value) => value.downcast_primitive(),
- Inner::Debug(value) => value.downcast_primitive(),
- Inner::Display(value) => value.downcast_primitive(),
-
- #[cfg(feature = "sval")]
- Inner::Sval(value) => value.downcast_primitive(),
- };
-
- primitive.map(Cast::Primitive).unwrap_or_else(|| {
- // If the erased value isn't a primitive then we visit it
- let mut cast = CastVisitor(Cast::Primitive(Primitive::None));
- let _ = self.visit(&mut cast);
- cast.0
- })
- }
-}
-
-pub(super) enum Cast<'v> {
- Primitive(Primitive<'v>),
- #[cfg(feature = "std")]
- String(String),
-}
-
-impl<'v> Cast<'v> {
- fn into_primitive(self) -> Primitive<'v> {
- match self {
- Cast::Primitive(value) => value,
- #[cfg(feature = "std")]
- _ => Primitive::None,
- }
- }
-}
-
-impl<'v> Primitive<'v> {
- fn into_borrowed_str(self) -> Option<&'v str> {
- if let Primitive::Str(value) = self {
- Some(value)
- } else {
- None
- }
- }
-
- fn into_u64(self) -> Option<u64> {
- match self {
- Primitive::Unsigned(value) => Some(value),
- Primitive::Signed(value) => Some(value as u64),
- Primitive::Float(value) => Some(value as u64),
- _ => None,
- }
- }
-
- fn into_i64(self) -> Option<i64> {
- match self {
- Primitive::Signed(value) => Some(value),
- Primitive::Unsigned(value) => Some(value as i64),
- Primitive::Float(value) => Some(value as i64),
- _ => None,
- }
- }
-
- fn into_f64(self) -> Option<f64> {
- match self {
- Primitive::Float(value) => Some(value),
- Primitive::Unsigned(value) => Some(value as f64),
- Primitive::Signed(value) => Some(value as f64),
- _ => None,
- }
- }
-
- fn into_char(self) -> Option<char> {
- if let Primitive::Char(value) = self {
- Some(value)
- } else {
- None
- }
- }
-
- fn into_bool(self) -> Option<bool> {
- if let Primitive::Bool(value) = self {
- Some(value)
- } else {
- None
- }
- }
-}
-
-impl<'v, T: ?Sized + 'static> Erased<'v, T> {
- // NOTE: This function is a perfect candidate for memoization
- // The outcome could be stored in a `Cell<Primitive>`
- fn downcast_primitive(self) -> Option<Primitive<'v>> {
- macro_rules! type_ids {
- ($($value:ident : $ty:ty => $cast:expr,)*) => {{
- struct TypeIds;
-
- impl TypeIds {
- fn downcast_primitive<'v, T: ?Sized>(&self, value: Erased<'v, T>) -> Option<Primitive<'v>> {
- $(
- if TypeId::of::<$ty>() == value.type_id {
- let $value = unsafe { value.downcast_unchecked::<$ty>() };
- return Some(Primitive::from($cast));
- }
- )*
-
- None
- }
- }
-
- TypeIds
- }};
- }
-
- let type_ids = type_ids![
- value: usize => *value as u64,
- value: u8 => *value as u64,
- value: u16 => *value as u64,
- value: u32 => *value as u64,
- value: u64 => *value,
-
- value: isize => *value as i64,
- value: i8 => *value as i64,
- value: i16 => *value as i64,
- value: i32 => *value as i64,
- value: i64 => *value,
-
- value: f32 => *value as f64,
- value: f64 => *value,
-
- value: char => *value,
- value: bool => *value,
-
- value: &str => *value,
- ];
-
- type_ids.downcast_primitive(self)
- }
-}
-
-#[cfg(feature = "std")]
-mod std_support {
- use super::*;
-
- use std::borrow::Cow;
-
- impl<'v> Value<'v> {
- /// Try get a `usize` from this value.
- ///
- /// This method is cheap for primitive types, but may call arbitrary
- /// serialization implementations for complex ones. If the serialization
- /// implementation produces a short lived string it will be allocated.
- pub fn to_str(&self) -> Option<Cow<str>> {
- self.inner.cast().into_str()
- }
- }
-
- impl<'v> Cast<'v> {
- pub(super) fn into_str(self) -> Option<Cow<'v, str>> {
- match self {
- Cast::Primitive(Primitive::Str(value)) => Some(value.into()),
- Cast::String(value) => Some(value.into()),
- _ => None,
- }
- }
- }
-
- #[cfg(test)]
- mod tests {
- use crate::kv::ToValue;
-
- #[test]
- fn primitive_cast() {
- assert_eq!(
- "a string",
- "a string"
- .to_owned()
- .to_value()
- .to_borrowed_str()
- .expect("invalid value")
- );
- assert_eq!(
- "a string",
- &*"a string".to_value().to_str().expect("invalid value")
- );
- assert_eq!(
- "a string",
- &*"a string"
- .to_owned()
- .to_value()
- .to_str()
- .expect("invalid value")
- );
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::kv::ToValue;
-
- #[test]
- fn primitive_cast() {
- assert_eq!(
- "a string",
- "a string"
- .to_value()
- .to_borrowed_str()
- .expect("invalid value")
- );
- assert_eq!(
- "a string",
- Some("a string")
- .to_value()
- .to_borrowed_str()
- .expect("invalid value")
- );
-
- assert_eq!(1u8, 1u64.to_value().to_u8().expect("invalid value"));
- assert_eq!(1u16, 1u64.to_value().to_u16().expect("invalid value"));
- assert_eq!(1u32, 1u64.to_value().to_u32().expect("invalid value"));
- assert_eq!(1u64, 1u64.to_value().to_u64().expect("invalid value"));
- assert_eq!(1usize, 1u64.to_value().to_usize().expect("invalid value"));
-
- assert_eq!(-1i8, -1i64.to_value().to_i8().expect("invalid value"));
- assert_eq!(-1i16, -1i64.to_value().to_i16().expect("invalid value"));
- assert_eq!(-1i32, -1i64.to_value().to_i32().expect("invalid value"));
- assert_eq!(-1i64, -1i64.to_value().to_i64().expect("invalid value"));
- assert_eq!(-1isize, -1i64.to_value().to_isize().expect("invalid value"));
-
- assert!(1f32.to_value().to_f32().is_some(), "invalid value");
- assert!(1f64.to_value().to_f64().is_some(), "invalid value");
-
- assert_eq!(1u32, 1i64.to_value().to_u32().expect("invalid value"));
- assert_eq!(1i32, 1u64.to_value().to_i32().expect("invalid value"));
- assert!(1f32.to_value().to_i32().is_some(), "invalid value");
-
- assert_eq!('a', 'a'.to_value().to_char().expect("invalid value"));
- assert_eq!(true, true.to_value().to_bool().expect("invalid value"));
- }
-}
diff --git a/src/kv/value/internal/fmt.rs b/src/kv/value/internal/fmt.rs
deleted file mode 100644
index 66817d4..0000000
--- a/src/kv/value/internal/fmt.rs
+++ /dev/null
@@ -1,249 +0,0 @@
-//! Integration between `Value` and `std::fmt`.
-//!
-//! This module allows any `Value` to implement the `fmt::Debug` and `fmt::Display` traits,
-//! and for any `fmt::Debug` or `fmt::Display` to be captured as a `Value`.
-
-use std::fmt;
-
-use super::{Erased, Inner, Visitor};
-use crate::kv;
-use crate::kv::value::{Error, Slot};
-
-impl<'v> kv::Value<'v> {
- /// Get a value from a debuggable type.
- pub fn from_debug<T>(value: &'v T) -> Self
- where
- T: fmt::Debug + 'static,
- {
- kv::Value {
- inner: Inner::Debug(unsafe { Erased::new_unchecked::<T>(value) }),
- }
- }
-
- /// Get a value from a displayable type.
- pub fn from_display<T>(value: &'v T) -> Self
- where
- T: fmt::Display + 'static,
- {
- kv::Value {
- inner: Inner::Display(unsafe { Erased::new_unchecked::<T>(value) }),
- }
- }
-}
-
-impl<'s, 'f> Slot<'s, 'f> {
- /// Fill the slot with a debuggable value.
- ///
- /// The given value doesn't need to satisfy any particular lifetime constraints.
- ///
- /// # Panics
- ///
- /// Calling more than a single `fill` method on this slot will panic.
- pub fn fill_debug<T>(&mut self, value: T) -> Result<(), Error>
- where
- T: fmt::Debug,
- {
- self.fill(|visitor| visitor.debug(&value))
- }
-
- /// Fill the slot with a displayable value.
- ///
- /// The given value doesn't need to satisfy any particular lifetime constraints.
- ///
- /// # Panics
- ///
- /// Calling more than a single `fill` method on this slot will panic.
- pub fn fill_display<T>(&mut self, value: T) -> Result<(), Error>
- where
- T: fmt::Display,
- {
- self.fill(|visitor| visitor.display(&value))
- }
-}
-
-pub(in kv::value) use self::fmt::{Arguments, Debug, Display};
-
-impl<'v> fmt::Debug for kv::Value<'v> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- struct DebugVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>);
-
- impl<'a, 'b: 'a, 'v> Visitor<'v> for DebugVisitor<'a, 'b> {
- fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
- fmt::Debug::fmt(v, self.0)?;
-
- Ok(())
- }
-
- fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> {
- fmt::Display::fmt(v, self.0)?;
-
- Ok(())
- }
-
- fn u64(&mut self, v: u64) -> Result<(), Error> {
- fmt::Debug::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn i64(&mut self, v: i64) -> Result<(), Error> {
- fmt::Debug::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn f64(&mut self, v: f64) -> Result<(), Error> {
- fmt::Debug::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn bool(&mut self, v: bool) -> Result<(), Error> {
- fmt::Debug::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn char(&mut self, v: char) -> Result<(), Error> {
- fmt::Debug::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn str(&mut self, v: &str) -> Result<(), Error> {
- fmt::Debug::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn none(&mut self) -> Result<(), Error> {
- self.debug(&format_args!("None"))
- }
-
- #[cfg(feature = "kv_unstable_sval")]
- fn sval(&mut self, v: &dyn super::sval::Value) -> Result<(), Error> {
- super::sval::fmt(self.0, v)
- }
- }
-
- self.visit(&mut DebugVisitor(f)).map_err(|_| fmt::Error)?;
-
- Ok(())
- }
-}
-
-impl<'v> fmt::Display for kv::Value<'v> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- struct DisplayVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>);
-
- impl<'a, 'b: 'a, 'v> Visitor<'v> for DisplayVisitor<'a, 'b> {
- fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
- fmt::Debug::fmt(v, self.0)?;
-
- Ok(())
- }
-
- fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> {
- fmt::Display::fmt(v, self.0)?;
-
- Ok(())
- }
-
- fn u64(&mut self, v: u64) -> Result<(), Error> {
- fmt::Display::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn i64(&mut self, v: i64) -> Result<(), Error> {
- fmt::Display::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn f64(&mut self, v: f64) -> Result<(), Error> {
- fmt::Display::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn bool(&mut self, v: bool) -> Result<(), Error> {
- fmt::Display::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn char(&mut self, v: char) -> Result<(), Error> {
- fmt::Display::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn str(&mut self, v: &str) -> Result<(), Error> {
- fmt::Display::fmt(&v, self.0)?;
-
- Ok(())
- }
-
- fn none(&mut self) -> Result<(), Error> {
- self.debug(&format_args!("None"))
- }
-
- #[cfg(feature = "kv_unstable_sval")]
- fn sval(&mut self, v: &dyn super::sval::Value) -> Result<(), Error> {
- super::sval::fmt(self.0, v)
- }
- }
-
- self.visit(&mut DisplayVisitor(f)).map_err(|_| fmt::Error)?;
-
- Ok(())
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- use crate::kv::value::ToValue;
-
- #[test]
- fn fmt_cast() {
- assert_eq!(
- 42u32,
- kv::Value::from_debug(&42u64)
- .to_u32()
- .expect("invalid value")
- );
-
- assert_eq!(
- "a string",
- kv::Value::from_display(&"a string")
- .to_borrowed_str()
- .expect("invalid value")
- );
- }
-
- #[test]
- fn fmt_debug() {
- assert_eq!(
- format!("{:?}", "a string"),
- format!("{:?}", "a string".to_value()),
- );
-
- assert_eq!(
- format!("{:04?}", 42u64),
- format!("{:04?}", 42u64.to_value()),
- );
- }
-
- #[test]
- fn fmt_display() {
- assert_eq!(
- format!("{}", "a string"),
- format!("{}", "a string".to_value()),
- );
-
- assert_eq!(format!("{:04}", 42u64), format!("{:04}", 42u64.to_value()),);
- }
-}
diff --git a/src/kv/value/internal/mod.rs b/src/kv/value/internal/mod.rs
deleted file mode 100644
index 01b72a7..0000000
--- a/src/kv/value/internal/mod.rs
+++ /dev/null
@@ -1,181 +0,0 @@
-//! The internal `Value` serialization API.
-//!
-//! This implementation isn't intended to be public. It may need to change
-//! for optimizations or to support new external serialization frameworks.
-
-use std::any::TypeId;
-
-use super::{Error, Fill, Slot};
-
-pub(super) mod cast;
-pub(super) mod fmt;
-#[cfg(feature = "kv_unstable_sval")]
-pub(super) mod sval;
-
-/// A container for a structured value for a specific kind of visitor.
-#[derive(Clone, Copy)]
-pub(super) enum Inner<'v> {
- /// A simple primitive value that can be copied without allocating.
- Primitive(Primitive<'v>),
- /// A value that can be filled.
- Fill(Erased<'v, dyn Fill + 'static>),
- /// A debuggable value.
- Debug(Erased<'v, dyn fmt::Debug + 'static>),
- /// A displayable value.
- Display(Erased<'v, dyn fmt::Display + 'static>),
-
- #[cfg(feature = "kv_unstable_sval")]
- /// A structured value from `sval`.
- Sval(Erased<'v, dyn sval::Value + 'static>),
-}
-
-impl<'v> Inner<'v> {
- pub(super) fn visit(self, visitor: &mut dyn Visitor<'v>) -> Result<(), Error> {
- match self {
- Inner::Primitive(value) => value.visit(visitor),
- Inner::Fill(value) => value.get().fill(&mut Slot::new(visitor)),
- Inner::Debug(value) => visitor.debug(value.get()),
- Inner::Display(value) => visitor.display(value.get()),
-
- #[cfg(feature = "kv_unstable_sval")]
- Inner::Sval(value) => visitor.sval(value.get()),
- }
- }
-}
-
-/// The internal serialization contract.
-pub(super) trait Visitor<'v> {
- fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error>;
- fn display(&mut self, v: &dyn fmt::Display) -> Result<(), Error> {
- self.debug(&format_args!("{}", v))
- }
-
- fn u64(&mut self, v: u64) -> Result<(), Error>;
- fn i64(&mut self, v: i64) -> Result<(), Error>;
- fn f64(&mut self, v: f64) -> Result<(), Error>;
- fn bool(&mut self, v: bool) -> Result<(), Error>;
- fn char(&mut self, v: char) -> Result<(), Error>;
-
- fn str(&mut self, v: &str) -> Result<(), Error>;
- fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
- self.str(v)
- }
-
- fn none(&mut self) -> Result<(), Error>;
-
- #[cfg(feature = "kv_unstable_sval")]
- fn sval(&mut self, v: &dyn sval::Value) -> Result<(), Error>;
-}
-
-/// A captured primitive value.
-///
-/// These values are common and cheap to copy around.
-#[derive(Clone, Copy)]
-pub(super) enum Primitive<'v> {
- Signed(i64),
- Unsigned(u64),
- Float(f64),
- Bool(bool),
- Char(char),
- Str(&'v str),
- Fmt(fmt::Arguments<'v>),
- None,
-}
-
-impl<'v> Primitive<'v> {
- fn visit(self, visitor: &mut dyn Visitor<'v>) -> Result<(), Error> {
- match self {
- Primitive::Signed(value) => visitor.i64(value),
- Primitive::Unsigned(value) => visitor.u64(value),
- Primitive::Float(value) => visitor.f64(value),
- Primitive::Bool(value) => visitor.bool(value),
- Primitive::Char(value) => visitor.char(value),
- Primitive::Str(value) => visitor.borrowed_str(value),
- Primitive::Fmt(value) => visitor.debug(&value),
- Primitive::None => visitor.none(),
- }
- }
-}
-
-impl<'v> From<u64> for Primitive<'v> {
- fn from(v: u64) -> Self {
- Primitive::Unsigned(v)
- }
-}
-
-impl<'v> From<i64> for Primitive<'v> {
- fn from(v: i64) -> Self {
- Primitive::Signed(v)
- }
-}
-
-impl<'v> From<f64> for Primitive<'v> {
- fn from(v: f64) -> Self {
- Primitive::Float(v)
- }
-}
-
-impl<'v> From<bool> for Primitive<'v> {
- fn from(v: bool) -> Self {
- Primitive::Bool(v)
- }
-}
-
-impl<'v> From<char> for Primitive<'v> {
- fn from(v: char) -> Self {
- Primitive::Char(v)
- }
-}
-
-impl<'v> From<&'v str> for Primitive<'v> {
- fn from(v: &'v str) -> Self {
- Primitive::Str(v)
- }
-}
-
-impl<'v> From<fmt::Arguments<'v>> for Primitive<'v> {
- fn from(v: fmt::Arguments<'v>) -> Self {
- Primitive::Fmt(v)
- }
-}
-
-/// A downcastable dynamic type.
-pub(super) struct Erased<'v, T: ?Sized> {
- type_id: TypeId,
- inner: &'v T,
-}
-
-impl<'v, T: ?Sized> Clone for Erased<'v, T> {
- fn clone(&self) -> Self {
- Erased {
- type_id: self.type_id,
- inner: self.inner,
- }
- }
-}
-
-impl<'v, T: ?Sized> Copy for Erased<'v, T> {}
-
-impl<'v, T: ?Sized> Erased<'v, T> {
- // SAFETY: `U: Unsize<T>` and the underlying value `T` must not change
- // We could add a safe variant of this method with the `Unsize` trait
- pub(super) unsafe fn new_unchecked<U>(inner: &'v T) -> Self
- where
- U: 'static,
- T: 'static,
- {
- Erased {
- type_id: TypeId::of::<U>(),
- inner,
- }
- }
-
- pub(super) fn get(self) -> &'v T {
- self.inner
- }
-
- // SAFETY: The underlying type of `T` is `U`
- pub(super) unsafe fn downcast_unchecked<U>(self) -> &'v U {
- &*(self.inner as *const T as *const U)
- }
-}
diff --git a/src/kv/value/internal/sval.rs b/src/kv/value/internal/sval.rs
deleted file mode 100644
index 618a036..0000000
--- a/src/kv/value/internal/sval.rs
+++ /dev/null
@@ -1,210 +0,0 @@
-//! Integration between `Value` and `sval`.
-//!
-//! This module allows any `Value` to implement the `sval::Value` trait,
-//! and for any `sval::Value` to be captured as a `Value`.
-
-extern crate sval;
-
-use std::fmt;
-
-use super::cast::Cast;
-use super::{Erased, Inner, Primitive, Visitor};
-use crate::kv;
-use crate::kv::value::{Error, Slot};
-
-impl<'v> kv::Value<'v> {
- /// Get a value from a structured type.
- pub fn from_sval<T>(value: &'v T) -> Self
- where
- T: sval::Value + 'static,
- {
- kv::Value {
- inner: Inner::Sval(unsafe { Erased::new_unchecked::<T>(value) }),
- }
- }
-}
-
-impl<'s, 'f> Slot<'s, 'f> {
- /// Fill the slot with a structured value.
- ///
- /// The given value doesn't need to satisfy any particular lifetime constraints.
- ///
- /// # Panics
- ///
- /// Calling more than a single `fill` method on this slot will panic.
- pub fn fill_sval<T>(&mut self, value: T) -> Result<(), Error>
- where
- T: sval::Value,
- {
- self.fill(|visitor| visitor.sval(&value))
- }
-}
-
-impl<'v> sval::Value for kv::Value<'v> {
- fn stream(&self, s: &mut sval::value::Stream) -> sval::value::Result {
- struct SvalVisitor<'a, 'b: 'a>(&'a mut sval::value::Stream<'b>);
-
- impl<'a, 'b: 'a, 'v> Visitor<'v> for SvalVisitor<'a, 'b> {
- fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
- self.0
- .fmt(format_args!("{:?}", v))
- .map_err(Error::from_sval)
- }
-
- fn u64(&mut self, v: u64) -> Result<(), Error> {
- self.0.u64(v).map_err(Error::from_sval)
- }
-
- fn i64(&mut self, v: i64) -> Result<(), Error> {
- self.0.i64(v).map_err(Error::from_sval)
- }
-
- fn f64(&mut self, v: f64) -> Result<(), Error> {
- self.0.f64(v).map_err(Error::from_sval)
- }
-
- fn bool(&mut self, v: bool) -> Result<(), Error> {
- self.0.bool(v).map_err(Error::from_sval)
- }
-
- fn char(&mut self, v: char) -> Result<(), Error> {
- self.0.char(v).map_err(Error::from_sval)
- }
-
- fn str(&mut self, v: &str) -> Result<(), Error> {
- self.0.str(v).map_err(Error::from_sval)
- }
-
- fn none(&mut self) -> Result<(), Error> {
- self.0.none().map_err(Error::from_sval)
- }
-
- fn sval(&mut self, v: &dyn sval::Value) -> Result<(), Error> {
- self.0.any(v).map_err(Error::from_sval)
- }
- }
-
- self.visit(&mut SvalVisitor(s)).map_err(Error::into_sval)?;
-
- Ok(())
- }
-}
-
-pub(in kv::value) use self::sval::Value;
-
-pub(super) fn fmt(f: &mut fmt::Formatter, v: &dyn sval::Value) -> Result<(), Error> {
- sval::fmt::debug(f, v)?;
- Ok(())
-}
-
-pub(super) fn cast<'v>(v: &dyn sval::Value) -> Cast<'v> {
- struct CastStream<'v>(Cast<'v>);
-
- impl<'v> sval::Stream for CastStream<'v> {
- fn u64(&mut self, v: u64) -> sval::stream::Result {
- self.0 = Cast::Primitive(Primitive::Unsigned(v));
- Ok(())
- }
-
- fn i64(&mut self, v: i64) -> sval::stream::Result {
- self.0 = Cast::Primitive(Primitive::Signed(v));
- Ok(())
- }
-
- fn f64(&mut self, v: f64) -> sval::stream::Result {
- self.0 = Cast::Primitive(Primitive::Float(v));
- Ok(())
- }
-
- fn char(&mut self, v: char) -> sval::stream::Result {
- self.0 = Cast::Primitive(Primitive::Char(v));
- Ok(())
- }
-
- fn bool(&mut self, v: bool) -> sval::stream::Result {
- self.0 = Cast::Primitive(Primitive::Bool(v));
- Ok(())
- }
-
- #[cfg(feature = "std")]
- fn str(&mut self, s: &str) -> sval::stream::Result {
- self.0 = Cast::String(s.into());
- Ok(())
- }
- }
-
- let mut cast = CastStream(Cast::Primitive(Primitive::None));
- let _ = sval::stream(&mut cast, v);
-
- cast.0
-}
-
-impl Error {
- fn from_sval(_: sval::value::Error) -> Self {
- Error::msg("`sval` serialization failed")
- }
-
- fn into_sval(self) -> sval::value::Error {
- sval::value::Error::msg("`sval` serialization failed")
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use kv::value::test::Token;
-
- #[test]
- fn test_from_sval() {
- assert_eq!(kv::Value::from_sval(&42u64).to_token(), Token::Sval);
- }
-
- #[test]
- fn test_sval_structured() {
- let value = kv::Value::from(42u64);
- let expected = vec![sval::test::Token::Unsigned(42)];
-
- assert_eq!(sval::test::tokens(value), expected);
- }
-
- #[test]
- fn sval_cast() {
- assert_eq!(
- 42u32,
- kv::Value::from_sval(&42u64)
- .to_u32()
- .expect("invalid value")
- );
-
- assert_eq!(
- "a string",
- kv::Value::from_sval(&"a string")
- .to_borrowed_str()
- .expect("invalid value")
- );
-
- #[cfg(feature = "std")]
- assert_eq!(
- "a string",
- kv::Value::from_sval(&"a string")
- .to_str()
- .expect("invalid value")
- );
- }
-
- #[test]
- fn sval_debug() {
- struct TestSval;
-
- impl sval::Value for TestSval {
- fn stream(&self, stream: &mut sval::value::Stream) -> sval::value::Result {
- stream.u64(42)
- }
- }
-
- assert_eq!(
- format!("{:04?}", 42u64),
- format!("{:04?}", kv::Value::from_sval(&TestSval)),
- );
- }
-}
diff --git a/src/kv/value/mod.rs b/src/kv/value/mod.rs
deleted file mode 100644
index 1d59dbf..0000000
--- a/src/kv/value/mod.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-//! Structured values.
-
-mod fill;
-mod impls;
-mod internal;
-
-#[cfg(test)]
-pub(in kv) mod test;
-
-pub use self::fill::{Fill, Slot};
-pub use kv::Error;
-
-use self::internal::{Inner, Primitive, Visitor};
-
-/// A type that can be converted into a [`Value`](struct.Value.html).
-pub trait ToValue {
- /// Perform the conversion.
- fn to_value(&self) -> Value;
-}
-
-impl<'a, T> ToValue for &'a T
-where
- T: ToValue + ?Sized,
-{
- fn to_value(&self) -> Value {
- (**self).to_value()
- }
-}
-
-impl<'v> ToValue for Value<'v> {
- fn to_value(&self) -> Value {
- Value { inner: self.inner }
- }
-}
-
-/// A value in a structured key-value pair.
-pub struct Value<'v> {
- inner: Inner<'v>,
-}
-
-impl<'v> Value<'v> {
- /// Get a value from an internal primitive.
- fn from_primitive<T>(value: T) -> Self
- where
- T: Into<Primitive<'v>>,
- {
- Value {
- inner: Inner::Primitive(value.into()),
- }
- }
-
- /// Visit the value using an internal visitor.
- fn visit<'a>(&'a self, visitor: &mut dyn Visitor<'a>) -> Result<(), Error> {
- self.inner.visit(visitor)
- }
-}
diff --git a/src/kv/value/test.rs b/src/kv/value/test.rs
deleted file mode 100644
index c8d6d5c..0000000
--- a/src/kv/value/test.rs
+++ /dev/null
@@ -1,81 +0,0 @@
-// Test support for inspecting Values
-
-use std::fmt;
-use std::str;
-
-use super::internal;
-use super::{Error, Value};
-
-#[derive(Debug, PartialEq)]
-pub(in kv) enum Token {
- U64(u64),
- I64(i64),
- F64(f64),
- Char(char),
- Bool(bool),
- Str(String),
- None,
-
- #[cfg(feature = "kv_unstable_sval")]
- Sval,
-}
-
-#[cfg(test)]
-impl<'v> Value<'v> {
- pub(in kv) fn to_token(&self) -> Token {
- struct TestVisitor(Option<Token>);
-
- impl<'v> internal::Visitor<'v> for TestVisitor {
- fn debug(&mut self, v: &dyn fmt::Debug) -> Result<(), Error> {
- self.0 = Some(Token::Str(format!("{:?}", v)));
- Ok(())
- }
-
- fn u64(&mut self, v: u64) -> Result<(), Error> {
- self.0 = Some(Token::U64(v));
- Ok(())
- }
-
- fn i64(&mut self, v: i64) -> Result<(), Error> {
- self.0 = Some(Token::I64(v));
- Ok(())
- }
-
- fn f64(&mut self, v: f64) -> Result<(), Error> {
- self.0 = Some(Token::F64(v));
- Ok(())
- }
-
- fn bool(&mut self, v: bool) -> Result<(), Error> {
- self.0 = Some(Token::Bool(v));
- Ok(())
- }
-
- fn char(&mut self, v: char) -> Result<(), Error> {
- self.0 = Some(Token::Char(v));
- Ok(())
- }
-
- fn str(&mut self, v: &str) -> Result<(), Error> {
- self.0 = Some(Token::Str(v.into()));
- Ok(())
- }
-
- fn none(&mut self) -> Result<(), Error> {
- self.0 = Some(Token::None);
- Ok(())
- }
-
- #[cfg(feature = "kv_unstable_sval")]
- fn sval(&mut self, _: &dyn internal::sval::Value) -> Result<(), Error> {
- self.0 = Some(Token::Sval);
- Ok(())
- }
- }
-
- let mut visitor = TestVisitor(None);
- self.visit(&mut visitor).unwrap();
-
- visitor.0.unwrap()
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 83b78d0..e754268 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -266,7 +266,7 @@
#![doc(
html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
- html_root_url = "https://docs.rs/log/0.4.11"
+ html_root_url = "https://docs.rs/log/0.4.14"
)]
#![warn(missing_docs)]
#![deny(missing_debug_implementations)]
@@ -288,7 +288,6 @@
use std::fmt;
use std::mem;
use std::str::FromStr;
-use std::sync::atomic::{AtomicUsize, Ordering};
#[macro_use]
mod macros;
@@ -297,6 +296,54 @@
#[cfg(feature = "kv_unstable")]
pub mod kv;
+#[cfg(has_atomics)]
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+#[cfg(not(has_atomics))]
+use std::cell::Cell;
+#[cfg(not(has_atomics))]
+use std::sync::atomic::Ordering;
+
+#[cfg(not(has_atomics))]
+struct AtomicUsize {
+ v: Cell<usize>,
+}
+
+#[cfg(not(has_atomics))]
+impl AtomicUsize {
+ const fn new(v: usize) -> AtomicUsize {
+ AtomicUsize { v: Cell::new(v) }
+ }
+
+ fn load(&self, _order: Ordering) -> usize {
+ self.v.get()
+ }
+
+ fn store(&self, val: usize, _order: Ordering) {
+ self.v.set(val)
+ }
+
+ #[cfg(atomic_cas)]
+ fn compare_exchange(
+ &self,
+ current: usize,
+ new: usize,
+ _success: Ordering,
+ _failure: Ordering,
+ ) -> Result<usize, usize> {
+ let prev = self.v.get();
+ if current == prev {
+ self.v.set(new);
+ }
+ Ok(prev)
+ }
+}
+
+// Any platform without atomics is unlikely to have multiple cores, so
+// writing via Cell will not be a race condition.
+#[cfg(not(has_atomics))]
+unsafe impl Sync for AtomicUsize {}
+
// The LOGGER static holds a pointer to the global logger. It is protected by
// the STATE static which determines whether LOGGER has been initialized yet.
static mut LOGGER: &dyn Log = &NopLogger;
@@ -479,7 +526,7 @@
impl fmt::Display for Level {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.pad(LOG_LEVEL_NAMES[*self as usize])
+ fmt.pad(self.as_str())
}
}
@@ -506,6 +553,13 @@
pub fn to_level_filter(&self) -> LevelFilter {
LevelFilter::from_usize(*self as usize).unwrap()
}
+
+ /// Returns the string representation of the `Level`.
+ ///
+ /// This returns the same string as the `fmt::Display` implementation.
+ pub fn as_str(&self) -> &'static str {
+ LOG_LEVEL_NAMES[*self as usize]
+ }
}
/// An enum representing the available verbosity level filters of the logger.
@@ -632,7 +686,7 @@
impl fmt::Display for LevelFilter {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
- fmt.pad(LOG_LEVEL_NAMES[*self as usize])
+ fmt.pad(self.as_str())
}
}
@@ -661,6 +715,13 @@
pub fn to_level(&self) -> Option<Level> {
Level::from_usize(*self as usize)
}
+
+ /// Returns the string representation of the `LevelFilter`.
+ ///
+ /// This returns the same string as the `fmt::Display` implementation.
+ pub fn as_str(&self) -> &'static str {
+ LOG_LEVEL_NAMES[*self as usize]
+ }
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
@@ -1153,6 +1214,23 @@
fn flush(&self) {}
}
+#[cfg(feature = "std")]
+impl<T> Log for std::boxed::Box<T>
+where
+ T: ?Sized + Log,
+{
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ self.as_ref().enabled(metadata)
+ }
+
+ fn log(&self, record: &Record) {
+ self.as_ref().log(record)
+ }
+ fn flush(&self) {
+ self.as_ref().flush()
+ }
+}
+
/// Sets the global maximum log level.
///
/// Generally, this should only be called by the active logging implementation.
@@ -1266,7 +1344,15 @@
where
F: FnOnce() -> &'static dyn Log,
{
- match STATE.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) {
+ let old_state = match STATE.compare_exchange(
+ UNINITIALIZED,
+ INITIALIZING,
+ Ordering::SeqCst,
+ Ordering::SeqCst,
+ ) {
+ Ok(s) | Err(s) => s,
+ };
+ match old_state {
UNINITIALIZED => {
unsafe {
LOGGER = make_logger();
@@ -1385,25 +1471,6 @@
// WARNING: this is not part of the crate's public API and is subject to change at any time
#[doc(hidden)]
-pub fn __private_api_log_lit(
- message: &str,
- level: Level,
- &(target, module_path, file, line): &(&str, &'static str, &'static str, u32),
-) {
- logger().log(
- &Record::builder()
- .args(format_args!("{}", message))
- .level(level)
- .target(target)
- .module_path_static(Some(module_path))
- .file_static(Some(file))
- .line(Some(line))
- .build(),
- );
-}
-
-// WARNING: this is not part of the crate's public API and is subject to change at any time
-#[doc(hidden)]
pub fn __private_api_enabled(level: Level, target: &str) -> bool {
logger().enabled(&Metadata::builder().level(level).target(target).build())
}
@@ -1497,6 +1564,20 @@
}
#[test]
+ fn test_level_as_str() {
+ let tests = &[
+ (Level::Error, "ERROR"),
+ (Level::Warn, "WARN"),
+ (Level::Info, "INFO"),
+ (Level::Debug, "DEBUG"),
+ (Level::Trace, "TRACE"),
+ ];
+ for (input, expected) in tests {
+ assert_eq!(*expected, input.as_str());
+ }
+ }
+
+ #[test]
fn test_level_show() {
assert_eq!("INFO", Level::Info.to_string());
assert_eq!("ERROR", Level::Error.to_string());
@@ -1536,6 +1617,21 @@
}
#[test]
+ fn test_level_filter_as_str() {
+ let tests = &[
+ (LevelFilter::Off, "OFF"),
+ (LevelFilter::Error, "ERROR"),
+ (LevelFilter::Warn, "WARN"),
+ (LevelFilter::Info, "INFO"),
+ (LevelFilter::Debug, "DEBUG"),
+ (LevelFilter::Trace, "TRACE"),
+ ];
+ for (input, expected) in tests {
+ assert_eq!(*expected, input.as_str());
+ }
+ }
+
+ #[test]
#[cfg(feature = "std")]
fn test_error_trait() {
use super::SetLoggerError;
diff --git a/src/macros.rs b/src/macros.rs
index 48de437..a234e04 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -29,18 +29,6 @@
/// ```
#[macro_export(local_inner_macros)]
macro_rules! log {
- (target: $target:expr, $lvl:expr, $message:expr) => ({
- let lvl = $lvl;
- if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() {
- // ensure that $message is a valid format string literal
- let _ = __log_format_args!($message);
- $crate::__private_api_log_lit(
- $message,
- lvl,
- &($target, __log_module_path!(), __log_file!(), __log_line!()),
- );
- }
- });
(target: $target:expr, $lvl:expr, $($arg:tt)+) => ({
let lvl = $lvl;
if lvl <= $crate::STATIC_MAX_LEVEL && lvl <= $crate::max_level() {
@@ -71,10 +59,10 @@
#[macro_export(local_inner_macros)]
macro_rules! error {
(target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Error, $($arg)+);
+ log!(target: $target, $crate::Level::Error, $($arg)+)
);
($($arg:tt)+) => (
- log!($crate::Level::Error, $($arg)+);
+ log!($crate::Level::Error, $($arg)+)
)
}
@@ -95,10 +83,10 @@
#[macro_export(local_inner_macros)]
macro_rules! warn {
(target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Warn, $($arg)+);
+ log!(target: $target, $crate::Level::Warn, $($arg)+)
);
($($arg:tt)+) => (
- log!($crate::Level::Warn, $($arg)+);
+ log!($crate::Level::Warn, $($arg)+)
)
}
@@ -121,10 +109,10 @@
#[macro_export(local_inner_macros)]
macro_rules! info {
(target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Info, $($arg)+);
+ log!(target: $target, $crate::Level::Info, $($arg)+)
);
($($arg:tt)+) => (
- log!($crate::Level::Info, $($arg)+);
+ log!($crate::Level::Info, $($arg)+)
)
}
@@ -146,10 +134,10 @@
#[macro_export(local_inner_macros)]
macro_rules! debug {
(target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Debug, $($arg)+);
+ log!(target: $target, $crate::Level::Debug, $($arg)+)
);
($($arg:tt)+) => (
- log!($crate::Level::Debug, $($arg)+);
+ log!($crate::Level::Debug, $($arg)+)
)
}
@@ -173,10 +161,10 @@
#[macro_export(local_inner_macros)]
macro_rules! trace {
(target: $target:expr, $($arg:tt)+) => (
- log!(target: $target, $crate::Level::Trace, $($arg)+);
+ log!(target: $target, $crate::Level::Trace, $($arg)+)
);
($($arg:tt)+) => (
- log!($crate::Level::Trace, $($arg)+);
+ log!($crate::Level::Trace, $($arg)+)
)
}
diff --git a/src/serde.rs b/src/serde.rs
index 8320ce1..e691321 100644
--- a/src/serde.rs
+++ b/src/serde.rs
@@ -60,6 +60,17 @@
self.visit_str(variant)
}
+
+ fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ let variant = LOG_LEVEL_NAMES[1..]
+ .get(v as usize)
+ .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?;
+
+ self.visit_str(variant)
+ }
}
impl<'de> DeserializeSeed<'de> for LevelIdentifier {
@@ -144,6 +155,17 @@
self.visit_str(variant)
}
+
+ fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
+ where
+ E: Error,
+ {
+ let variant = LOG_LEVEL_NAMES
+ .get(v as usize)
+ .ok_or_else(|| Error::invalid_value(Unexpected::Unsigned(v), &self))?;
+
+ self.visit_str(variant)
+ }
}
impl<'de> DeserializeSeed<'de> for LevelFilterIdentifier {
@@ -203,6 +225,14 @@
]
}
+ fn level_variant_tokens(variant: u32) -> [Token; 3] {
+ [
+ Token::Enum { name: "Level" },
+ Token::U32(variant),
+ Token::Unit,
+ ]
+ }
+
fn level_filter_token(variant: &'static str) -> Token {
Token::UnitVariant {
name: "LevelFilter",
@@ -220,6 +250,16 @@
]
}
+ fn level_filter_variant_tokens(variant: u32) -> [Token; 3] {
+ [
+ Token::Enum {
+ name: "LevelFilter",
+ },
+ Token::U32(variant),
+ Token::Unit,
+ ]
+ }
+
#[test]
fn test_level_ser_de() {
let cases = [
@@ -266,6 +306,21 @@
}
#[test]
+ fn test_level_de_variant_index() {
+ let cases = [
+ (Level::Error, level_variant_tokens(0)),
+ (Level::Warn, level_variant_tokens(1)),
+ (Level::Info, level_variant_tokens(2)),
+ (Level::Debug, level_variant_tokens(3)),
+ (Level::Trace, level_variant_tokens(4)),
+ ];
+
+ for &(value, tokens) in &cases {
+ assert_de_tokens(&value, &tokens);
+ }
+ }
+
+ #[test]
fn test_level_de_error() {
let msg = "unknown variant `errorx`, expected one of \
`ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`";
@@ -321,6 +376,22 @@
}
#[test]
+ fn test_level_filter_de_variant_index() {
+ let cases = [
+ (LevelFilter::Off, level_filter_variant_tokens(0)),
+ (LevelFilter::Error, level_filter_variant_tokens(1)),
+ (LevelFilter::Warn, level_filter_variant_tokens(2)),
+ (LevelFilter::Info, level_filter_variant_tokens(3)),
+ (LevelFilter::Debug, level_filter_variant_tokens(4)),
+ (LevelFilter::Trace, level_filter_variant_tokens(5)),
+ ];
+
+ for &(value, tokens) in &cases {
+ assert_de_tokens(&value, &tokens);
+ }
+ }
+
+ #[test]
fn test_level_filter_de_error() {
let msg = "unknown variant `errorx`, expected one of \
`OFF`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`";
diff --git a/tests/filters.rs b/tests/filters.rs
deleted file mode 100644
index 661c034..0000000
--- a/tests/filters.rs
+++ /dev/null
@@ -1,72 +0,0 @@
-#[macro_use]
-extern crate log;
-
-use log::{Level, LevelFilter, Log, Metadata, Record};
-use std::sync::{Arc, Mutex};
-
-#[cfg(feature = "std")]
-use log::set_boxed_logger;
-
-#[cfg(not(feature = "std"))]
-fn set_boxed_logger(logger: Box<dyn Log>) -> Result<(), log::SetLoggerError> {
- log::set_logger(Box::leak(logger))
-}
-
-struct State {
- last_log: Mutex<Option<Level>>,
-}
-
-struct Logger(Arc<State>);
-
-impl Log for Logger {
- fn enabled(&self, _: &Metadata) -> bool {
- true
- }
-
- fn log(&self, record: &Record) {
- *self.0.last_log.lock().unwrap() = Some(record.level());
- }
- fn flush(&self) {}
-}
-
-fn main() {
- let me = Arc::new(State {
- last_log: Mutex::new(None),
- });
- let a = me.clone();
- set_boxed_logger(Box::new(Logger(me))).unwrap();
-
- test(&a, LevelFilter::Off);
- test(&a, LevelFilter::Error);
- test(&a, LevelFilter::Warn);
- test(&a, LevelFilter::Info);
- test(&a, LevelFilter::Debug);
- test(&a, LevelFilter::Trace);
-}
-
-fn test(a: &State, filter: LevelFilter) {
- log::set_max_level(filter);
- error!("");
- last(&a, t(Level::Error, filter));
- warn!("");
- last(&a, t(Level::Warn, filter));
- info!("");
- last(&a, t(Level::Info, filter));
- debug!("");
- last(&a, t(Level::Debug, filter));
- trace!("");
- last(&a, t(Level::Trace, filter));
-
- fn t(lvl: Level, filter: LevelFilter) -> Option<Level> {
- if lvl <= filter {
- Some(lvl)
- } else {
- None
- }
- }
-}
-
-fn last(state: &State, expected: Option<Level>) {
- let lvl = state.last_log.lock().unwrap().take();
- assert_eq!(lvl, expected);
-}
diff --git a/tests/macros.rs b/tests/macros.rs
deleted file mode 100644
index e9dc133..0000000
--- a/tests/macros.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-#[macro_use]
-extern crate log;
-
-#[test]
-fn base() {
- info!("hello");
- info!("hello",);
-}
-
-#[test]
-fn base_expr_context() {
- let _ = info!("hello");
-}
-
-#[test]
-fn with_args() {
- info!("hello {}", "cats");
- info!("hello {}", "cats",);
- info!("hello {}", "cats",);
-}
-
-#[test]
-fn with_args_expr_context() {
- match "cats" {
- cats => info!("hello {}", cats),
- };
-}
-
-#[test]
-fn with_named_args() {
- let cats = "cats";
-
- info!("hello {cats}", cats = cats);
- info!("hello {cats}", cats = cats,);
- info!("hello {cats}", cats = cats,);
-}