value_bag/
owned.rs

1use crate::{
2    internal::{self, Internal},
3    std::sync::Arc,
4    ValueBag,
5};
6
7/// A dynamic structured value.
8///
9/// This type is an owned variant of [`ValueBag`] that can be
10/// constructed using its [`to_owned`](struct.ValueBag.html#method.to_owned) method.
11/// `OwnedValueBag`s are suitable for storing and sharing across threads.
12///
13/// `OwnedValueBag`s can be inspected by converting back into a regular `ValueBag`
14/// using the [`by_ref`](#method.by_ref) method.
15#[derive(Clone)]
16pub struct OwnedValueBag {
17    inner: internal::owned::OwnedInternal,
18}
19
20impl<'v> ValueBag<'v> {
21    /// Buffer this value into an [`OwnedValueBag`].
22    pub fn to_owned(&self) -> OwnedValueBag {
23        OwnedValueBag {
24            inner: self.inner.to_owned(),
25        }
26    }
27
28    /// Buffer this value into an [`OwnedValueBag`], internally storing it
29    /// in an `Arc` for cheap cloning.
30    pub fn to_shared(&self) -> OwnedValueBag {
31        self.to_owned().into_shared()
32    }
33}
34
35impl ValueBag<'static> {
36    /// Get a value from an owned, sharable, debuggable type.
37    ///
38    /// This method will attempt to capture the given value as a well-known primitive
39    /// before resorting to using its `Debug` implementation.
40    ///
41    /// The value will be stored in an `Arc` for cheap cloning.
42    pub fn capture_shared_debug<T>(value: T) -> Self
43    where
44        T: internal::fmt::Debug + Send + Sync + 'static,
45    {
46        Self::try_capture_owned(&value).unwrap_or_else(|| ValueBag {
47            inner: Internal::SharedDebug(Arc::new(value)),
48        })
49    }
50
51    /// Get a value from an owned, sharable, displayable type.
52    ///
53    /// This method will attempt to capture the given value as a well-known primitive
54    /// before resorting to using its `Display` implementation.
55    ///
56    /// The value will be stored in an `Arc` for cheap cloning.
57    pub fn capture_shared_display<T>(value: T) -> Self
58    where
59        T: internal::fmt::Display + Send + Sync + 'static,
60    {
61        Self::try_capture_owned(&value).unwrap_or_else(|| ValueBag {
62            inner: Internal::SharedDisplay(Arc::new(value)),
63        })
64    }
65
66    /// Get a value from an owned, shared error.
67    ///
68    /// The value will be stored in an `Arc` for cheap cloning.
69    #[cfg(feature = "error")]
70    pub fn capture_shared_error<T>(value: T) -> Self
71    where
72        T: internal::error::Error + Send + Sync + 'static,
73    {
74        ValueBag {
75            inner: Internal::SharedError(Arc::new(value)),
76        }
77    }
78
79    /// Get a value from an owned, shared, structured type.
80    ///
81    /// This method will attempt to capture the given value as a well-known primitive
82    /// before resorting to using its `Value` implementation.
83    ///
84    /// The value will be stored in an `Arc` for cheap cloning.
85    #[cfg(feature = "sval2")]
86    pub fn capture_shared_sval2<T>(value: T) -> Self
87    where
88        T: value_bag_sval2::lib::Value + Send + Sync + 'static,
89    {
90        Self::try_capture_owned(&value).unwrap_or(ValueBag {
91            inner: Internal::SharedSval2(Arc::new(value)),
92        })
93    }
94
95    /// Get a value from an owned, shared, structured type.
96    ///
97    /// This method will attempt to capture the given value as a well-known primitive
98    /// before resorting to using its `Value` implementation.
99    ///
100    /// The value will be stored in an `Arc` for cheap cloning.
101    #[cfg(feature = "serde1")]
102    pub fn capture_shared_serde1<T>(value: T) -> Self
103    where
104        T: value_bag_serde1::lib::Serialize + Send + Sync + 'static,
105    {
106        Self::try_capture_owned(&value).unwrap_or(ValueBag {
107            inner: Internal::SharedSerde1(Arc::new(value)),
108        })
109    }
110
111    /// Get a value from an owned, shared, sequence.
112    ///
113    /// The value will be stored in an `Arc` for cheap cloning.
114    #[cfg(feature = "seq")]
115    pub fn capture_shared_seq_slice<I, T>(value: I) -> Self
116    where
117        I: AsRef<[T]> + Send + Sync + 'static,
118        T: Send + Sync + 'static,
119        for<'v> &'v T: Into<ValueBag<'v>>,
120    {
121        use crate::{
122            internal::seq::Visitor,
123            std::{marker::PhantomData, ops::ControlFlow},
124        };
125
126        struct OwnedSeqSlice<I: ?Sized, T>(PhantomData<[T]>, I);
127
128        impl<I, T> internal::seq::Seq for OwnedSeqSlice<I, T>
129        where
130            I: AsRef<[T]> + ?Sized,
131            for<'v> &'v T: Into<ValueBag<'v>>,
132        {
133            fn visit(&self, visitor: &mut dyn Visitor<'_>) {
134                for v in self.1.as_ref().iter() {
135                    if let ControlFlow::Break(()) = visitor.element(v.into()) {
136                        return;
137                    }
138                }
139            }
140        }
141
142        Self::try_capture_owned(&value).unwrap_or(ValueBag {
143            inner: Internal::SharedSeq(Arc::new(OwnedSeqSlice(PhantomData, value))),
144        })
145    }
146}
147
148impl OwnedValueBag {
149    /// Get a regular [`ValueBag`] from this type.
150    ///
151    /// Once a `ValueBag` has been buffered, it will behave
152    /// slightly differently when converted back:
153    ///
154    /// - `fmt::Debug` won't use formatting flags.
155    /// - `serde::Serialize` will use the text-based representation.
156    /// - The original type may change, so downcasting can stop producing results.
157    pub const fn by_ref(&self) -> ValueBag<'_> {
158        ValueBag {
159            inner: self.inner.by_ref(),
160        }
161    }
162
163    /// Make this value cheap to clone and share by internally storing it in an `Arc`.
164    ///
165    /// If the value is already shared then this method will simply clone it.
166    pub fn into_shared(self) -> Self {
167        OwnedValueBag {
168            inner: self.inner.into_shared(),
169        }
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    #[cfg(target_arch = "wasm32")]
176    use wasm_bindgen_test::*;
177
178    use super::*;
179
180    use crate::{
181        fill,
182        std::{mem, string::ToString},
183    };
184
185    const SIZE_LIMIT_U64: usize = 4;
186
187    #[test]
188    fn is_send_sync() {
189        fn assert<T: Send + Sync + 'static>() {}
190
191        assert::<OwnedValueBag>();
192    }
193
194    #[test]
195    fn owned_value_bag_size() {
196        let size = mem::size_of::<OwnedValueBag>();
197        let limit = mem::size_of::<u64>() * SIZE_LIMIT_U64;
198
199        if size > limit {
200            panic!(
201                "`OwnedValueBag` size ({} bytes) is too large (expected up to {} bytes)",
202                size, limit,
203            );
204        }
205    }
206
207    #[test]
208    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
209    fn fill_to_owned() {
210        let value = ValueBag::from_fill(&|slot: fill::Slot| slot.fill_any(42u64)).to_owned();
211
212        assert!(matches!(
213            value.inner,
214            internal::owned::OwnedInternal::BigUnsigned(42)
215        ));
216    }
217
218    #[test]
219    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
220    fn fill_to_shared() {
221        let value = ValueBag::from_fill(&|slot: fill::Slot| slot.fill_any(42u64)).to_shared();
222
223        assert!(matches!(
224            value.inner,
225            internal::owned::OwnedInternal::BigUnsigned(42)
226        ));
227    }
228
229    #[test]
230    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
231    fn fmt_to_owned() {
232        let debug = ValueBag::from_debug(&"a value").to_owned();
233        let display = ValueBag::from_display(&"a value").to_owned();
234
235        assert!(matches!(
236            debug.inner,
237            internal::owned::OwnedInternal::Debug(_)
238        ));
239        assert!(matches!(
240            display.inner,
241            internal::owned::OwnedInternal::Display(_)
242        ));
243
244        assert_eq!("\"a value\"", debug.to_string());
245        assert_eq!("a value", display.to_string());
246
247        let debug = debug.by_ref();
248        let display = display.by_ref();
249
250        assert!(matches!(debug.inner, internal::Internal::AnonDebug(_)));
251        assert!(matches!(display.inner, internal::Internal::AnonDisplay(_)));
252    }
253
254    #[test]
255    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
256    fn fmt_to_shared() {
257        let debug = ValueBag::from_debug(&"a value").to_shared();
258        let display = ValueBag::from_display(&"a value").to_shared();
259
260        assert!(matches!(
261            debug.inner,
262            internal::owned::OwnedInternal::SharedDebug(_)
263        ));
264        assert!(matches!(
265            display.inner,
266            internal::owned::OwnedInternal::SharedDisplay(_)
267        ));
268    }
269
270    #[test]
271    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
272    fn owned_fmt_to_owned() {
273        let debug = ValueBag::capture_shared_debug("a value".to_string()).to_owned();
274        let display = ValueBag::capture_shared_display("a value".to_string()).to_owned();
275
276        assert!(matches!(
277            debug.inner,
278            internal::owned::OwnedInternal::SharedDebug(_)
279        ));
280        assert!(matches!(
281            display.inner,
282            internal::owned::OwnedInternal::SharedDisplay(_)
283        ));
284
285        assert_eq!("\"a value\"", debug.to_string());
286        assert_eq!("a value", display.to_string());
287
288        let debug = debug.by_ref();
289        let display = display.by_ref();
290
291        assert!(matches!(debug.inner, internal::Internal::SharedRefDebug(_)));
292        assert!(matches!(
293            display.inner,
294            internal::Internal::SharedRefDisplay(_)
295        ));
296    }
297
298    #[test]
299    #[cfg(feature = "error")]
300    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
301    fn error_to_owned() {
302        use crate::std::io;
303
304        let value =
305            ValueBag::from_dyn_error(&io::Error::new(io::ErrorKind::Other, "something failed!"))
306                .to_owned();
307
308        assert!(matches!(
309            value.inner,
310            internal::owned::OwnedInternal::Error(_)
311        ));
312
313        let value = value.by_ref();
314
315        assert!(matches!(value.inner, internal::Internal::AnonError(_)));
316
317        assert!(value.to_borrowed_error().is_some());
318    }
319
320    #[test]
321    #[cfg(feature = "error")]
322    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
323    fn error_to_shared() {
324        use crate::std::io;
325
326        let value =
327            ValueBag::from_dyn_error(&io::Error::new(io::ErrorKind::Other, "something failed!"))
328                .to_shared();
329
330        assert!(matches!(
331            value.inner,
332            internal::owned::OwnedInternal::SharedError(_)
333        ));
334    }
335
336    #[test]
337    #[cfg(feature = "error")]
338    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
339    fn owned_error_to_owned() {
340        use crate::std::io;
341
342        let value = ValueBag::capture_shared_error(io::Error::new(
343            io::ErrorKind::Other,
344            "something failed!",
345        ))
346        .to_owned();
347
348        assert!(matches!(
349            value.inner,
350            internal::owned::OwnedInternal::SharedError(_)
351        ));
352
353        let value = value.by_ref();
354
355        assert!(matches!(value.inner, internal::Internal::SharedRefError(_)));
356    }
357
358    #[test]
359    #[cfg(feature = "serde1")]
360    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
361    fn serde1_to_owned() {
362        let value = ValueBag::from_serde1(&42u64).to_owned();
363
364        assert!(matches!(
365            value.inner,
366            internal::owned::OwnedInternal::Serde1(_)
367        ));
368
369        let value = value.by_ref();
370
371        assert!(matches!(value.inner, internal::Internal::AnonSerde1(_)));
372    }
373
374    #[test]
375    #[cfg(feature = "serde1")]
376    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
377    fn serde1_to_shared() {
378        let value = ValueBag::from_serde1(&42u64).to_shared();
379
380        assert!(matches!(
381            value.inner,
382            internal::owned::OwnedInternal::SharedSerde1(_)
383        ));
384    }
385
386    #[test]
387    #[cfg(feature = "serde1")]
388    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
389    fn owned_serde1_to_owned() {
390        let value = ValueBag::capture_shared_serde1("a value".to_string()).to_owned();
391
392        assert!(matches!(
393            value.inner,
394            internal::owned::OwnedInternal::SharedSerde1(_)
395        ));
396
397        let value = value.by_ref();
398
399        assert!(matches!(
400            value.inner,
401            internal::Internal::SharedRefSerde1(_)
402        ));
403    }
404
405    #[test]
406    #[cfg(feature = "sval2")]
407    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
408    fn sval2_to_owned() {
409        let value = ValueBag::from_sval2(&42u64).to_owned();
410
411        assert!(matches!(
412            value.inner,
413            internal::owned::OwnedInternal::Sval2(_)
414        ));
415
416        let value = value.by_ref();
417
418        assert!(matches!(value.inner, internal::Internal::AnonSval2(_)));
419    }
420
421    #[test]
422    #[cfg(feature = "sval2")]
423    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
424    fn sval2_to_shared() {
425        let value = ValueBag::from_sval2(&42u64).to_shared();
426
427        assert!(matches!(
428            value.inner,
429            internal::owned::OwnedInternal::SharedSval2(_)
430        ));
431    }
432
433    #[test]
434    #[cfg(feature = "sval2")]
435    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
436    fn owned_sval2_to_owned() {
437        let value = ValueBag::capture_shared_sval2("a value".to_string()).to_owned();
438
439        assert!(matches!(
440            value.inner,
441            internal::owned::OwnedInternal::SharedSval2(_)
442        ));
443
444        let value = value.by_ref();
445
446        assert!(matches!(value.inner, internal::Internal::SharedRefSval2(_)));
447    }
448
449    #[test]
450    #[cfg(feature = "seq")]
451    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
452    fn seq_to_owned() {
453        let value = ValueBag::from_seq_slice(&[1, 2, 3]).to_owned();
454
455        assert!(matches!(
456            value.inner,
457            internal::owned::OwnedInternal::Seq(_)
458        ));
459
460        let value = value.by_ref();
461
462        assert!(matches!(value.inner, internal::Internal::AnonSeq(_)));
463    }
464
465    #[test]
466    #[cfg(feature = "seq")]
467    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
468    fn seq_to_shared() {
469        let value = ValueBag::from_seq_slice(&[1, 2, 3]).to_shared();
470
471        assert!(matches!(
472            value.inner,
473            internal::owned::OwnedInternal::SharedSeq(_)
474        ));
475    }
476
477    #[test]
478    #[cfg(feature = "seq")]
479    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
480    fn owned_seq_to_owned() {
481        let value = ValueBag::capture_shared_seq_slice(vec![1, 2, 3]).to_owned();
482
483        assert!(matches!(
484            value.inner,
485            internal::owned::OwnedInternal::SharedSeq(_)
486        ));
487
488        let value = value.by_ref();
489
490        assert!(matches!(value.inner, internal::Internal::SharedRefSeq(_)));
491    }
492}