Skip to main content

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    #[cfg(not(feature = "inline-str-l"))]
186    const SIZE_LIMIT_U64: usize = 4;
187    #[cfg(feature = "inline-str-l")]
188    const SIZE_LIMIT_U64: usize = 6;
189
190    #[test]
191    fn is_send_sync() {
192        fn assert<T: Send + Sync + 'static>() {}
193
194        assert::<OwnedValueBag>();
195    }
196
197    #[test]
198    fn owned_value_bag_size() {
199        let size = mem::size_of::<OwnedValueBag>();
200        let limit = mem::size_of::<u64>() * SIZE_LIMIT_U64;
201
202        if size > limit {
203            panic!(
204                "`OwnedValueBag` size ({} bytes) is too large (expected up to {} bytes)",
205                size, limit,
206            );
207        }
208    }
209
210    #[test]
211    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
212    fn fill_to_owned() {
213        let value = ValueBag::from_fill(&|slot: fill::Slot| slot.fill_any(42u64)).to_owned();
214
215        assert!(matches!(
216            value.inner,
217            internal::owned::OwnedInternal::BigUnsigned(42)
218        ));
219    }
220
221    #[test]
222    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
223    fn fill_to_shared() {
224        let value = ValueBag::from_fill(&|slot: fill::Slot| slot.fill_any(42u64)).to_shared();
225
226        assert!(matches!(
227            value.inner,
228            internal::owned::OwnedInternal::BigUnsigned(42)
229        ));
230    }
231
232    #[test]
233    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
234    fn fmt_to_owned() {
235        let debug = ValueBag::from_debug(&"a value").to_owned();
236        let display = ValueBag::from_display(&"a value").to_owned();
237
238        #[cfg(not(feature = "inline-str"))]
239        {
240            assert!(matches!(
241                debug.inner,
242                internal::owned::OwnedInternal::Debug(_)
243            ));
244            assert!(matches!(
245                display.inner,
246                internal::owned::OwnedInternal::Display(_)
247            ));
248        }
249        #[cfg(feature = "inline-str")]
250        {
251            assert!(matches!(
252                debug.inner,
253                internal::owned::OwnedInternal::SmallDebug(_)
254            ));
255            assert!(matches!(
256                display.inner,
257                internal::owned::OwnedInternal::SmallDisplay(_)
258            ));
259        }
260
261        assert_eq!("\"a value\"", debug.to_string());
262        assert_eq!("a value", display.to_string());
263
264        let debug = debug.by_ref();
265        let display = display.by_ref();
266
267        assert!(matches!(debug.inner, internal::Internal::AnonDebug(_)));
268        assert!(matches!(display.inner, internal::Internal::AnonDisplay(_)));
269    }
270
271    #[test]
272    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
273    fn fmt_to_owned_l() {
274        let debug =
275            ValueBag::from_debug(&"a value that occupies too much space to ever be stored inline")
276                .to_owned();
277        let display = ValueBag::from_display(
278            &"a value that occupies too much space to ever be stored inline",
279        )
280        .to_owned();
281
282        assert!(matches!(
283            debug.inner,
284            internal::owned::OwnedInternal::Debug(_)
285        ));
286        assert!(matches!(
287            display.inner,
288            internal::owned::OwnedInternal::Display(_)
289        ));
290
291        assert_eq!(
292            "\"a value that occupies too much space to ever be stored inline\"",
293            debug.to_string()
294        );
295        assert_eq!(
296            "a value that occupies too much space to ever be stored inline",
297            display.to_string()
298        );
299
300        let debug = debug.by_ref();
301        let display = display.by_ref();
302
303        assert!(matches!(debug.inner, internal::Internal::AnonDebug(_)));
304        assert!(matches!(display.inner, internal::Internal::AnonDisplay(_)));
305    }
306
307    #[test]
308    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
309    fn fmt_to_shared() {
310        let debug = ValueBag::from_debug(&"a value").to_shared();
311        let display = ValueBag::from_display(&"a value").to_shared();
312
313        #[cfg(not(feature = "inline-str"))]
314        {
315            assert!(matches!(
316                debug.inner,
317                internal::owned::OwnedInternal::SharedDebug(_)
318            ));
319            assert!(matches!(
320                display.inner,
321                internal::owned::OwnedInternal::SharedDisplay(_)
322            ));
323        }
324        #[cfg(feature = "inline-str")]
325        {
326            assert!(matches!(
327                debug.inner,
328                internal::owned::OwnedInternal::SmallDebug(_)
329            ));
330            assert!(matches!(
331                display.inner,
332                internal::owned::OwnedInternal::SmallDisplay(_)
333            ));
334        }
335    }
336
337    #[test]
338    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
339    fn owned_fmt_to_owned() {
340        let debug = ValueBag::capture_shared_debug("a value".to_string()).to_owned();
341        let display = ValueBag::capture_shared_display("a value".to_string()).to_owned();
342
343        assert!(matches!(
344            debug.inner,
345            internal::owned::OwnedInternal::SharedDebug(_)
346        ));
347        assert!(matches!(
348            display.inner,
349            internal::owned::OwnedInternal::SharedDisplay(_)
350        ));
351
352        assert_eq!("\"a value\"", debug.to_string());
353        assert_eq!("a value", display.to_string());
354
355        let debug = debug.by_ref();
356        let display = display.by_ref();
357
358        assert!(matches!(debug.inner, internal::Internal::SharedRefDebug(_)));
359        assert!(matches!(
360            display.inner,
361            internal::Internal::SharedRefDisplay(_)
362        ));
363    }
364
365    #[test]
366    #[cfg(feature = "error")]
367    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
368    fn error_to_owned() {
369        use crate::std::io;
370
371        let value =
372            ValueBag::from_dyn_error(&io::Error::new(io::ErrorKind::Other, "something failed!"))
373                .to_owned();
374
375        assert!(matches!(
376            value.inner,
377            internal::owned::OwnedInternal::Error(_)
378        ));
379
380        let value = value.by_ref();
381
382        assert!(matches!(value.inner, internal::Internal::AnonError(_)));
383
384        assert!(value.to_borrowed_error().is_some());
385    }
386
387    #[test]
388    #[cfg(feature = "error")]
389    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
390    fn error_to_shared() {
391        use crate::std::io;
392
393        let value =
394            ValueBag::from_dyn_error(&io::Error::new(io::ErrorKind::Other, "something failed!"))
395                .to_shared();
396
397        assert!(matches!(
398            value.inner,
399            internal::owned::OwnedInternal::SharedError(_)
400        ));
401    }
402
403    #[test]
404    #[cfg(feature = "error")]
405    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
406    fn owned_error_to_owned() {
407        use crate::std::io;
408
409        let value = ValueBag::capture_shared_error(io::Error::new(
410            io::ErrorKind::Other,
411            "something failed!",
412        ))
413        .to_owned();
414
415        assert!(matches!(
416            value.inner,
417            internal::owned::OwnedInternal::SharedError(_)
418        ));
419
420        let value = value.by_ref();
421
422        assert!(matches!(value.inner, internal::Internal::SharedRefError(_)));
423    }
424
425    #[test]
426    #[cfg(feature = "serde1")]
427    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
428    fn serde1_to_owned() {
429        let value = ValueBag::from_serde1(&42u64).to_owned();
430
431        assert!(matches!(
432            value.inner,
433            internal::owned::OwnedInternal::Serde1(_)
434        ));
435
436        let value = value.by_ref();
437
438        assert!(matches!(value.inner, internal::Internal::AnonSerde1(_)));
439    }
440
441    #[test]
442    #[cfg(feature = "serde1")]
443    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
444    fn serde1_to_shared() {
445        let value = ValueBag::from_serde1(&42u64).to_shared();
446
447        assert!(matches!(
448            value.inner,
449            internal::owned::OwnedInternal::SharedSerde1(_)
450        ));
451    }
452
453    #[test]
454    #[cfg(feature = "serde1")]
455    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
456    fn owned_serde1_to_owned() {
457        let value = ValueBag::capture_shared_serde1("a value".to_string()).to_owned();
458
459        assert!(matches!(
460            value.inner,
461            internal::owned::OwnedInternal::SharedSerde1(_)
462        ));
463
464        let value = value.by_ref();
465
466        assert!(matches!(
467            value.inner,
468            internal::Internal::SharedRefSerde1(_)
469        ));
470    }
471
472    #[test]
473    #[cfg(feature = "sval2")]
474    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
475    fn sval2_to_owned() {
476        let value = ValueBag::from_sval2(&42u64).to_owned();
477
478        assert!(matches!(
479            value.inner,
480            internal::owned::OwnedInternal::Sval2(_)
481        ));
482
483        let value = value.by_ref();
484
485        assert!(matches!(value.inner, internal::Internal::AnonSval2(_)));
486    }
487
488    #[test]
489    #[cfg(feature = "sval2")]
490    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
491    fn sval2_to_shared() {
492        let value = ValueBag::from_sval2(&42u64).to_shared();
493
494        assert!(matches!(
495            value.inner,
496            internal::owned::OwnedInternal::SharedSval2(_)
497        ));
498    }
499
500    #[test]
501    #[cfg(feature = "sval2")]
502    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
503    fn owned_sval2_to_owned() {
504        let value = ValueBag::capture_shared_sval2("a value".to_string()).to_owned();
505
506        assert!(matches!(
507            value.inner,
508            internal::owned::OwnedInternal::SharedSval2(_)
509        ));
510
511        let value = value.by_ref();
512
513        assert!(matches!(value.inner, internal::Internal::SharedRefSval2(_)));
514    }
515
516    #[test]
517    #[cfg(feature = "seq")]
518    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
519    fn seq_to_owned() {
520        let value = ValueBag::from_seq_slice(&[1, 2, 3]).to_owned();
521
522        assert!(matches!(
523            value.inner,
524            internal::owned::OwnedInternal::Seq(_)
525        ));
526
527        let value = value.by_ref();
528
529        assert!(matches!(value.inner, internal::Internal::AnonSeq(_)));
530    }
531
532    #[test]
533    #[cfg(feature = "seq")]
534    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
535    fn seq_to_shared() {
536        let value = ValueBag::from_seq_slice(&[1, 2, 3]).to_shared();
537
538        assert!(matches!(
539            value.inner,
540            internal::owned::OwnedInternal::SharedSeq(_)
541        ));
542    }
543
544    #[test]
545    #[cfg(feature = "seq")]
546    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
547    fn owned_seq_to_owned() {
548        let value = ValueBag::capture_shared_seq_slice(vec![1, 2, 3]).to_owned();
549
550        assert!(matches!(
551            value.inner,
552            internal::owned::OwnedInternal::SharedSeq(_)
553        ));
554
555        let value = value.by_ref();
556
557        assert!(matches!(value.inner, internal::Internal::SharedRefSeq(_)));
558    }
559}