value_bag/
fill.rs

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