value_bag/fill.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
//! Deferred value initialization.
//!
//! The [`Fill`] trait is a way to bridge APIs that may not be directly
//! compatible with other constructor methods.
//!
//! The `Fill` trait is automatically implemented for closures, so can usually
//! be used in libraries that can't implement the trait themselves.
//!
//! ```
//! use value_bag::{ValueBag, fill::Slot};
//!
//! let value = ValueBag::from_fill(&|slot: Slot| {
//! #[derive(Debug)]
//! struct MyShortLivedValue;
//!
//! slot.fill_debug(&MyShortLivedValue)
//! });
//!
//! assert_eq!("MyShortLivedValue", format!("{:?}", value));
//! ```
//!
//! The trait can also be implemented manually:
//!
//! ```
//! # use std::fmt::Debug;
//! use value_bag::{ValueBag, Error, fill::{Slot, Fill}};
//!
//! struct FillDebug;
//!
//! impl Fill for FillDebug {
//! fn fill(&self, slot: Slot) -> Result<(), Error> {
//! slot.fill_debug(&42i64 as &dyn Debug)
//! }
//! }
//!
//! let value = ValueBag::from_fill(&FillDebug);
//!
//! assert_eq!(None, value.to_i64());
//! ```
use crate::std::fmt;
use super::internal::{Internal, InternalVisitor};
use super::{Error, ValueBag};
impl<'v> ValueBag<'v> {
/// Get a value from a fillable slot.
pub const fn from_fill<T>(value: &'v T) -> Self
where
T: Fill,
{
ValueBag {
inner: Internal::Fill(value),
}
}
}
/// A type that requires extra work to convert into a [`ValueBag`](../struct.ValueBag.html).
///
/// This trait is an advanced initialization API.
/// 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: Slot) -> Result<(), Error>;
}
impl<F> Fill for F
where
F: Fn(Slot) -> Result<(), Error>,
{
fn fill(&self, slot: Slot) -> Result<(), Error> {
(self)(slot)
}
}
/// A value slot to fill using the [`Fill`](trait.Fill.html) trait.
pub struct Slot<'s, 'f> {
visitor: &'s mut dyn InternalVisitor<'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(crate) fn new(visitor: &'s mut dyn InternalVisitor<'f>) -> Self {
Slot { visitor }
}
pub(crate) fn fill<F>(self, f: F) -> Result<(), Error>
where
F: FnOnce(&mut dyn InternalVisitor<'f>) -> Result<(), Error>,
{
f(self.visitor)
}
/// Fill the slot with a value.
///
/// The given value doesn't need to satisfy any particular lifetime constraints.
pub fn fill_any<T>(self, value: T) -> Result<(), Error>
where
T: Into<ValueBag<'f>>,
{
self.fill(|visitor| value.into().inner.internal_visit(visitor))
}
}
#[cfg(test)]
mod tests {
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;
use super::*;
use crate::std::string::ToString;
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn fill_value_borrowed() {
struct TestFill;
impl Fill for TestFill {
fn fill(&self, slot: Slot) -> Result<(), Error> {
let dbg = &1 as &dyn fmt::Debug;
slot.fill_debug(dbg)
}
}
assert_eq!("1", ValueBag::from_fill(&TestFill).to_string());
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn fill_cast() {
struct TestFill;
impl Fill for TestFill {
fn fill(&self, slot: Slot) -> Result<(), Error> {
slot.fill_any("a string")
}
}
assert_eq!(
"a string",
ValueBag::from_fill(&TestFill)
.to_borrowed_str()
.expect("invalid value")
);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn fill_fn_cast() {
assert_eq!(
42u64,
ValueBag::from_fill(&|slot: Slot| slot.fill_any(42u64))
.to_u64()
.unwrap()
);
}
#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn fill_fn_borrowed() {
#[derive(Debug)]
struct MyValue;
let value = MyValue;
assert_eq!(
format!("{:?}", value),
format!(
"{:?}",
ValueBag::from_fill(&|slot: Slot| slot.fill_debug(&value))
)
);
}
}