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 | |
41 | use crate::std::fmt; |
42 | |
43 | use super::internal::{Internal, InternalVisitor}; |
44 | use super::{Error, ValueBag}; |
45 | |
46 | impl<'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. |
63 | pub trait Fill { |
64 | /// Fill a value. |
65 | fn fill(&self, slot: Slot) -> Result<(), Error>; |
66 | } |
67 | |
68 | impl<F> Fill for F |
69 | where |
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. |
78 | pub struct Slot<'s, 'f> { |
79 | visitor: &'s mut dyn InternalVisitor<'f>, |
80 | } |
81 | |
82 | impl<'s, 'f> fmt::Debug for Slot<'s, 'f> { |
83 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
84 | f.debug_struct(name:"Slot" ).finish() |
85 | } |
86 | } |
87 | |
88 | impl<'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: &mut (dyn InternalVisitor<'f> + 'static)| value.into().inner.internal_visit(visitor)) |
108 | } |
109 | } |
110 | |
111 | #[cfg (test)] |
112 | mod 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 | } |
181 | |