blob: 2e7489935118f82edbbe5cb0ccb83a18bd38daf8 [file] [log] [blame]
//! 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)),
)
}
}