Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support coercing structured Values into primitive types #379

Merged
merged 5 commits into from
Feb 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ kv_unstable_sval = ["kv_unstable", "sval/fmt"]
[dependencies]
cfg-if = "0.1.2"
serde = { version = "1.0", optional = true, default-features = false }
sval = { version = "0.4.2", optional = true, default-features = false }
sval = { version = "0.5", optional = true, default-features = false }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This minor bump is fine because this is all unstable.


[dev-dependencies]
serde_test = "1.0"
sval = { version = "0.4.2", features = ["test"] }
sval = { version = "0.5", features = ["test"] }
148 changes: 148 additions & 0 deletions src/kv/value/fill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//! Lazy value initialization.

use std::fmt;

use super::internal::{Erased, Inner, Visitor};
use super::{Error, Value};

impl<'v> Value<'v> {
/// Get a value from a fillable slot.
pub fn from_fill<T>(value: &'v T) -> Self
where
T: Fill + 'static,
{
Value {
inner: Inner::Fill(unsafe { Erased::new_unchecked::<T>(value) }),
}
}
}

/// A type that requires extra work to convert into a [`Value`](struct.Value.html).
///
/// This trait is a more advanced initialization API than [`ToValue`](trait.ToValue.html).
/// It's intended for erased values coming from other logging frameworks that may need
/// to perform extra work to determine the concrete type to use.
pub trait Fill {
/// Fill a value.
fn fill(&self, slot: &mut Slot) -> Result<(), Error>;
}

impl<'a, T> Fill for &'a T
where
T: Fill + ?Sized,
{
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
(**self).fill(slot)
}
}

/// A value slot to fill using the [`Fill`](trait.Fill.html) trait.
pub struct Slot<'s, 'f> {
filled: bool,
visitor: &'s mut dyn Visitor<'f>,
}

impl<'s, 'f> fmt::Debug for Slot<'s, 'f> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Slot").finish()
}
}

impl<'s, 'f> Slot<'s, 'f> {
pub(super) fn new(visitor: &'s mut dyn Visitor<'f>) -> Self {
Slot {
visitor,
filled: false,
}
}

pub(super) fn fill<F>(&mut self, f: F) -> Result<(), Error>
where
F: FnOnce(&mut dyn Visitor<'f>) -> Result<(), Error>,
{
assert!(!self.filled, "the slot has already been filled");
self.filled = true;

f(self.visitor)
}

/// Fill the slot with a value.
///
/// The given value doesn't need to satisfy any particular lifetime constraints.
///
/// # Panics
///
/// Calling more than a single `fill` method on this slot will panic.
pub fn fill_any<T>(&mut self, value: T) -> Result<(), Error>
where
T: Into<Value<'f>>,
{
self.fill(|visitor| value.into().inner.visit(visitor))
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn fill_value_borrowed() {
struct TestFill;

impl Fill for TestFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
let dbg: &dyn fmt::Debug = &1;

slot.fill_debug(&dbg)
}
}

assert_eq!("1", Value::from_fill(&TestFill).to_string());
}

#[test]
fn fill_value_owned() {
struct TestFill;

impl Fill for TestFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
slot.fill_any("a string")
}
}
}

#[test]
#[should_panic]
fn fill_multiple_times_panics() {
struct BadFill;

impl Fill for BadFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
slot.fill_any(42)?;
slot.fill_any(6789)?;

Ok(())
}
}

let _ = Value::from_fill(&BadFill).to_string();
}

#[test]
fn fill_cast() {
struct TestFill;

impl Fill for TestFill {
fn fill(&self, slot: &mut Slot) -> Result<(), Error> {
slot.fill_any("a string")
}
}

assert_eq!(
"a string",
Value::from_fill(&TestFill)
.to_borrowed_str()
.expect("invalid value")
);
}
}
Loading