value_bag/
lib.rs

1//! Structured values.
2//!
3//! This crate contains the [`ValueBag`] type, a container for an anonymous structured value.
4//! `ValueBag`s can be captured in various ways and then formatted, inspected, and serialized
5//! without losing their original structure.
6//!
7//! The producer of a [`ValueBag`] may use a different strategy for capturing than the eventual
8//! consumer. They don't need to coordinate directly.
9
10#![doc(html_root_url = "https://docs.rs/value-bag/1.10.0")]
11#![no_std]
12#![allow(
13    clippy::unnecessary_fallible_conversions,
14    clippy::explicit_auto_deref,
15    clippy::wrong_self_convention
16)]
17
18/*
19# Crate design
20
21This library internally ties several frameworks together. The details of how
22this is done are hidden from end-users. It looks roughly like this:
23
24            ┌─────┐     ┌──────┐
25            │sval2│     │serde1│  1. libs on crates.io
26            └──┬──┘     └─┬─┬──┘
27               ├──────────┘ │
28       ┌───────▼──┐     ┌───▼───────┐
29       │meta/sval2│     │meta/serde1│  2. meta crates with features
30       └───────┬──┘     └───┬───────┘
31               │            │
32 ┌─────────────▼──┐     ┌───▼─────────────┐
33 │internal/sval/v2◄─────┤internal/serde/v1│  3. internal modules with `InternalVisitor`
34 └─────────────┬──┘     └───┬─────────────┘
35               │            │
36        ┌──────▼────────┬───▼────────────┐
37        │Internal::Sval2│Internal::Serde1│  4. variants in `Internal` enum
38        └───────────────┼────────────────┘
3940┌───────────────────────▼────────────────────────┐
41│ValueBag::capture_sval2│ValueBag::capture_serde1│  5. ctors on `ValueBag`
42└───────────────────────┼────────────────────────┘
4344┌───────────────────────▼───────────────────────────┐
45│impl Value for ValueBag│impl Serialize for ValueBag│  6. trait impls on `ValueBag`
46└───────────────────────┴───────────────────────────┘
47
48## 1. libs on crates.io
49
50These are the frameworks like `serde` or `sval`.
51
52## 2. meta crates with features
53
54These are crates that are internal to `value-bag`. They depend on the public
55framework and any utility crates that come along with it. They also expose
56features for any other framework. This is done this way so `value-bag` can use
57Cargo's `crate?/feature` syntax to conditionally add framework support.
58
59## 3. internal modules with `InternalVisitor`
60
61These are modules in `value-bag` that integrate the framework using the
62`InternalVisitor` trait. This makes it possible for that framework to cast
63primitive values and pass-through any other framework.
64
65## 4. variants in `Internal` enum
66
67These are individual variants on the `Internal` enum that the `ValueBag`
68type wraps. Each framework has one or more variants in this enum.
69
70## 5. ctors on `ValueBag`
71
72These are constructors for producers of `ValueBag`s that accept a value
73implementing a serialization trait from a specific framework, like
74`serde::Serialize` or `sval::Value`.
75
76## 7. trait impls on `ValueBag`
77
78These are trait impls for consumers of `ValueBag`s that serialize the
79underlying value, bridging it if it was produced for a different framework.
80*/
81
82#[cfg(any(feature = "std", test))]
83#[macro_use]
84#[allow(unused_imports)]
85extern crate std;
86
87#[cfg(all(not(test), feature = "alloc", not(feature = "std")))]
88#[macro_use]
89#[allow(unused_imports)]
90extern crate core;
91
92#[cfg(all(not(test), feature = "alloc", not(feature = "std")))]
93#[macro_use]
94#[allow(unused_imports)]
95extern crate alloc;
96
97#[cfg(all(not(test), feature = "alloc", not(feature = "std")))]
98#[allow(unused_imports)]
99mod std {
100    pub use crate::{
101        alloc::{borrow, boxed, string, vec},
102        core::*,
103    };
104
105    #[cfg(feature = "owned")]
106    pub use crate::alloc::sync;
107}
108
109#[cfg(not(any(feature = "alloc", feature = "std", test)))]
110#[macro_use]
111#[allow(unused_imports)]
112extern crate core as std;
113
114mod error;
115pub mod fill;
116mod impls;
117mod internal;
118pub mod visit;
119
120#[cfg(any(test, feature = "test"))]
121pub mod test;
122
123#[cfg(feature = "owned")]
124mod owned;
125#[cfg(feature = "owned")]
126pub use self::owned::*;
127
128pub use self::error::Error;
129
130/// A dynamic structured value.
131///
132/// # Capturing values
133///
134/// There are a few ways to capture a value:
135///
136/// - Using the `ValueBag::capture_*` and `ValueBag::from_*` methods.
137/// - Using the standard `From` trait.
138/// - Using the `Fill` API.
139///
140/// ## Using the `ValueBag::capture_*` methods
141///
142/// `ValueBag` offers a few constructor methods that capture values of different kinds.
143/// These methods require a `T: 'static` to support downcasting.
144///
145/// ```
146/// use value_bag::ValueBag;
147///
148/// let value = ValueBag::capture_debug(&42i32);
149///
150/// assert_eq!(Some(42), value.to_i64());
151/// ```
152///
153/// Capturing a value using these methods will retain type information so that
154/// the contents of the bag can be serialized using an appropriate type.
155///
156/// For cases where the `'static` bound can't be satisfied, there's also a few
157/// constructors that exclude it.
158///
159/// ```
160/// # use std::fmt::Debug;
161/// use value_bag::ValueBag;
162///
163/// let value = ValueBag::from_debug(&42i32);
164///
165/// assert_eq!(None, value.to_i64());
166/// ```
167///
168/// These `ValueBag::from_*` methods are lossy though and `ValueBag::capture_*` should be preferred.
169///
170/// ## Using the standard `From` trait
171///
172/// Primitive types can be converted into a `ValueBag` using the standard `From` trait.
173///
174/// ```
175/// use value_bag::ValueBag;
176///
177/// let value = ValueBag::from(42i32);
178///
179/// assert_eq!(Some(42), value.to_i64());
180/// ```
181///
182/// ## Using the `Fill` API
183///
184/// The [`fill`] module provides a way to bridge APIs that may not be directly
185/// compatible with other constructor methods.
186///
187/// The `Fill` trait is automatically implemented for closures, so can usually
188/// be used in libraries that can't implement the trait themselves.
189///
190/// ```
191/// use value_bag::{ValueBag, fill::Slot};
192///
193/// let value = ValueBag::from_fill(&|slot: Slot| {
194///     #[derive(Debug)]
195///     struct MyShortLivedValue;
196///
197///     slot.fill_debug(&MyShortLivedValue)
198/// });
199///
200/// assert_eq!("MyShortLivedValue", format!("{:?}", value));
201/// ```
202///
203/// The trait can also be implemented manually:
204///
205/// ```
206/// # use std::fmt::Debug;
207/// use value_bag::{ValueBag, Error, fill::{Slot, Fill}};
208///
209/// struct FillDebug;
210///
211/// impl Fill for FillDebug {
212///     fn fill(&self, slot: Slot) -> Result<(), Error> {
213///         slot.fill_debug(&42i32 as &dyn Debug)
214///     }
215/// }
216///
217/// let value = ValueBag::from_fill(&FillDebug);
218///
219/// assert_eq!(None, value.to_i64());
220/// ```
221///
222/// # Inspecting values
223///
224/// Once you have a `ValueBag` there are also a few ways to inspect it:
225///
226/// - Using `std::fmt`
227/// - Using `sval`
228/// - Using `serde`
229/// - Using the `ValueBag::visit` method.
230/// - Using the `ValueBag::to_*` methods.
231/// - Using the `ValueBag::downcast_ref` method.
232///
233/// ## Using the `ValueBag::visit` method
234///
235/// The [`visit`] module provides a simple visitor API that can be used to inspect
236/// the structure of primitives stored in a `ValueBag`.
237/// More complex datatypes can then be handled using `std::fmt`, `sval`, or `serde`.
238///
239/// ```
240/// #[cfg(not(feature = "std"))] fn main() {}
241/// #[cfg(feature = "std")]
242/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
243/// # fn escape(buf: &[u8]) -> &[u8] { buf }
244/// # fn itoa_fmt<T>(num: T) -> Vec<u8> { vec![] }
245/// # fn ryu_fmt<T>(num: T) -> Vec<u8> { vec![] }
246/// # use std::io::Write;
247/// use value_bag::{ValueBag, Error, visit::Visit};
248///
249/// // Implement some simple custom serialization
250/// struct MyVisit(Vec<u8>);
251/// impl<'v> Visit<'v> for MyVisit {
252///     fn visit_any(&mut self, v: ValueBag) -> Result<(), Error> {
253///         // Fallback to `Debug` if we didn't visit the value specially
254///         write!(&mut self.0, "{:?}", v).map_err(|_| Error::msg("failed to write value"))
255///     }
256///
257///     fn visit_u64(&mut self, v: u64) -> Result<(), Error> {
258///         self.0.extend_from_slice(itoa_fmt(v).as_slice());
259///         Ok(())
260///     }
261///
262///     fn visit_i64(&mut self, v: i64) -> Result<(), Error> {
263///         self.0.extend_from_slice(itoa_fmt(v).as_slice());
264///         Ok(())
265///     }
266///
267///     fn visit_f64(&mut self, v: f64) -> Result<(), Error> {
268///         self.0.extend_from_slice(ryu_fmt(v).as_slice());
269///         Ok(())
270///     }
271///
272///     fn visit_str(&mut self, v: &str) -> Result<(), Error> {
273///         self.0.push(b'\"');
274///         self.0.extend_from_slice(escape(v.as_bytes()));
275///         self.0.push(b'\"');
276///         Ok(())
277///     }
278///
279///     fn visit_bool(&mut self, v: bool) -> Result<(), Error> {
280///         self.0.extend_from_slice(if v { b"true" } else { b"false" });
281///         Ok(())
282///     }
283/// }
284///
285/// let value = ValueBag::from(42i64);
286///
287/// let mut visitor = MyVisit(vec![]);
288/// value.visit(&mut visitor)?;
289/// # Ok(())
290/// # }
291/// ```
292///
293/// ## Using `std::fmt`
294///
295/// Any `ValueBag` can be formatted using the `std::fmt` machinery as either `Debug`
296/// or `Display`.
297///
298/// ```
299/// use value_bag::ValueBag;
300///
301/// let value = ValueBag::from(true);
302///
303/// assert_eq!("true", format!("{:?}", value));
304/// ```
305///
306/// ## Using `sval`
307///
308/// When the `sval2` feature is enabled, any `ValueBag` can be serialized using `sval`.
309/// This makes it possible to visit any typed structure captured in the `ValueBag`,
310/// including complex datatypes like maps and sequences.
311///
312/// `sval` doesn't need to allocate so can be used in no-std environments.
313///
314/// First, enable the `sval2` feature in your `Cargo.toml`:
315///
316/// ```toml
317/// [dependencies.value-bag]
318/// features = ["sval2"]
319/// ```
320///
321/// Then stream the contents of the `ValueBag` using `sval`.
322///
323/// ```
324/// # #[cfg(not(all(feature = "std", feature = "sval2")))] fn main() {}
325/// # #[cfg(all(feature = "std", feature = "sval2"))]
326/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
327/// # use value_bag_sval2::json as sval_json;
328/// use value_bag::ValueBag;
329///
330/// let value = ValueBag::from(42i64);
331/// let json = sval_json::stream_to_string(value)?;
332/// # Ok(())
333/// # }
334/// ```
335///
336/// ## Using `serde`
337///
338/// When the `serde1` feature is enabled, any `ValueBag` can be serialized using `serde`.
339/// This makes it possible to visit any typed structure captured in the `ValueBag`,
340/// including complex datatypes like maps and sequences.
341///
342/// `serde` needs a few temporary allocations, so also brings in the `std` feature.
343///
344/// First, enable the `serde1` feature in your `Cargo.toml`:
345///
346/// ```toml
347/// [dependencies.value-bag]
348/// features = ["serde1"]
349/// ```
350///
351/// Then stream the contents of the `ValueBag` using `serde`.
352///
353/// ```
354/// # #[cfg(not(all(feature = "std", feature = "serde1")))] fn main() {}
355/// # #[cfg(all(feature = "std", feature = "serde1"))]
356/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
357/// # use value_bag_serde1::json as serde_json;
358/// use value_bag::ValueBag;
359///
360/// let value = ValueBag::from(42i64);
361/// let json = serde_json::to_string(&value)?;
362/// # Ok(())
363/// # }
364/// ```
365///
366/// Also see [`serde.rs`](https://serde.rs) for more examples of writing your own serializers.
367///
368/// ## Using the `ValueBag::to_*` methods
369///
370/// `ValueBag` provides a set of methods for attempting to pull a concrete value out.
371/// These are useful for ad-hoc analysis but aren't intended for exhaustively serializing
372/// the contents of a `ValueBag`.
373///
374/// ```
375/// use value_bag::ValueBag;
376///
377/// let value = ValueBag::capture_display(&42u64);
378///
379/// assert_eq!(Some(42u64), value.to_u64());
380/// ```
381///
382/// ## Using the `ValueBag::downcast_ref` method
383///
384/// When a `ValueBag` is created using one of the `capture_*` constructors, it can be downcast
385/// back to its original value.
386/// This can also be useful for ad-hoc analysis where there's a common possible non-primitive
387/// type that could be captured.
388///
389/// ```
390/// # #[derive(Debug)] struct SystemTime;
391/// # fn now() -> SystemTime { SystemTime }
392/// use value_bag::ValueBag;
393///
394/// let timestamp = now();
395/// let value = ValueBag::capture_debug(&timestamp);
396///
397/// assert!(value.downcast_ref::<SystemTime>().is_some());
398/// ```
399///
400/// # Working with sequences
401///
402/// The `seq` feature of `value-bag` enables utilities for working with values that are sequences.
403/// First, enable the `seq` feature in your `Cargo.toml`:
404///
405/// ```toml
406/// [dependencies.value-bag]
407/// features = ["seq"]
408/// ```
409///
410/// Slices and arrays can be captured as sequences:
411///
412/// ```
413/// # #[cfg(not(all(feature = "serde1", feature = "seq")))] fn main() {}
414/// # #[cfg(all(feature = "serde1", feature = "seq"))]
415/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
416/// # use value_bag_serde1::json as serde_json;
417/// use value_bag::ValueBag;
418///
419/// let value = ValueBag::from_seq_slice(&[1, 2, 3]);
420///
421/// assert_eq!("[1,2,3]", serde_json::to_string(&value)?);
422/// # Ok(())
423/// # }
424/// ```
425///
426/// A sequence captured with either `sval` or `serde` can have its elements extracted:
427///
428/// ```
429/// # #[cfg(not(all(feature = "serde1", feature = "seq")))] fn main() {}
430/// # #[cfg(all(feature = "serde1", feature = "seq"))]
431/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
432/// use value_bag::ValueBag;
433///
434/// let value = ValueBag::from_serde1(&[1.0, 2.0, 3.0]);
435///
436/// let seq = value.to_f64_seq::<Vec<Option<f64>>>().ok_or("not a sequence")?;
437///
438/// assert_eq!(vec![Some(1.0), Some(2.0), Some(3.0)], seq);
439/// # Ok(())
440/// # }
441/// ```
442#[derive(Clone)]
443pub struct ValueBag<'v> {
444    inner: internal::Internal<'v>,
445}
446
447impl<'v> ValueBag<'v> {
448    /// Get an empty `ValueBag`.
449    #[inline]
450    pub const fn empty() -> ValueBag<'v> {
451        ValueBag {
452            inner: internal::Internal::None,
453        }
454    }
455
456    /// Get a `ValueBag` from an `Option`.
457    ///
458    /// This method will return `ValueBag::empty` if the value is `None`.
459    #[inline]
460    pub fn from_option(v: Option<impl Into<ValueBag<'v>>>) -> ValueBag<'v> {
461        match v {
462            Some(v) => v.into(),
463            None => ValueBag::empty(),
464        }
465    }
466
467    /// Get a `ValueBag` from a `u8`.
468    #[inline]
469    pub const fn from_u8(v: u8) -> ValueBag<'v> {
470        ValueBag {
471            inner: internal::Internal::Unsigned(v as u64),
472        }
473    }
474
475    /// Get a `ValueBag` from a `u16`.
476    #[inline]
477    pub const fn from_u16(v: u16) -> ValueBag<'v> {
478        ValueBag {
479            inner: internal::Internal::Unsigned(v as u64),
480        }
481    }
482
483    /// Get a `ValueBag` from a `u32`.
484    #[inline]
485    pub const fn from_u32(v: u32) -> ValueBag<'v> {
486        ValueBag {
487            inner: internal::Internal::Unsigned(v as u64),
488        }
489    }
490
491    /// Get a `ValueBag` from a `u64`.
492    #[inline]
493    pub const fn from_u64(v: u64) -> ValueBag<'v> {
494        ValueBag {
495            inner: internal::Internal::Unsigned(v),
496        }
497    }
498
499    /// Get a `ValueBag` from a `usize`.
500    #[inline]
501    pub const fn from_usize(v: usize) -> ValueBag<'v> {
502        ValueBag {
503            inner: internal::Internal::Unsigned(v as u64),
504        }
505    }
506
507    /// Get a `ValueBag` from a `u128`.
508    #[inline]
509    pub const fn from_u128_ref(v: &'v u128) -> ValueBag<'v> {
510        ValueBag {
511            #[cfg(not(feature = "inline-i128"))]
512            inner: internal::Internal::BigUnsigned(v),
513            #[cfg(feature = "inline-i128")]
514            inner: internal::Internal::BigUnsigned(*v),
515        }
516    }
517
518    /// Get a `ValueBag` from a `u128`.
519    #[inline]
520    #[cfg(feature = "inline-i128")]
521    pub const fn from_u128(v: u128) -> ValueBag<'v> {
522        ValueBag {
523            inner: internal::Internal::BigUnsigned(v),
524        }
525    }
526
527    /// Get a `ValueBag` from a `i8`.
528    #[inline]
529    pub const fn from_i8(v: i8) -> ValueBag<'v> {
530        ValueBag {
531            inner: internal::Internal::Signed(v as i64),
532        }
533    }
534
535    /// Get a `ValueBag` from a `i16`.
536    #[inline]
537    pub const fn from_i16(v: i16) -> ValueBag<'v> {
538        ValueBag {
539            inner: internal::Internal::Signed(v as i64),
540        }
541    }
542
543    /// Get a `ValueBag` from a `i32`.
544    #[inline]
545    pub const fn from_i32(v: i32) -> ValueBag<'v> {
546        ValueBag {
547            inner: internal::Internal::Signed(v as i64),
548        }
549    }
550
551    /// Get a `ValueBag` from a `i64`.
552    #[inline]
553    pub const fn from_i64(v: i64) -> ValueBag<'v> {
554        ValueBag {
555            inner: internal::Internal::Signed(v),
556        }
557    }
558
559    /// Get a `ValueBag` from a `isize`.
560    #[inline]
561    pub const fn from_isize(v: isize) -> ValueBag<'v> {
562        ValueBag {
563            inner: internal::Internal::Signed(v as i64),
564        }
565    }
566
567    /// Get a `ValueBag` from a `i128`.
568    #[inline]
569    pub const fn from_i128_ref(v: &'v i128) -> ValueBag<'v> {
570        ValueBag {
571            #[cfg(not(feature = "inline-i128"))]
572            inner: internal::Internal::BigSigned(v),
573            #[cfg(feature = "inline-i128")]
574            inner: internal::Internal::BigSigned(*v),
575        }
576    }
577
578    /// Get a `ValueBag` from a `i128`.
579    #[inline]
580    #[cfg(feature = "inline-i128")]
581    pub const fn from_i128(v: i128) -> ValueBag<'v> {
582        ValueBag {
583            inner: internal::Internal::BigSigned(v),
584        }
585    }
586
587    /// Get a `ValueBag` from a `f32`.
588    #[inline]
589    pub const fn from_f32(v: f32) -> ValueBag<'v> {
590        ValueBag {
591            inner: internal::Internal::Float(v as f64),
592        }
593    }
594
595    /// Get a `ValueBag` from a `f64`.
596    #[inline]
597    pub const fn from_f64(v: f64) -> ValueBag<'v> {
598        ValueBag {
599            inner: internal::Internal::Float(v),
600        }
601    }
602
603    /// Get a `ValueBag` from a `bool`.
604    #[inline]
605    pub const fn from_bool(v: bool) -> ValueBag<'v> {
606        ValueBag {
607            inner: internal::Internal::Bool(v),
608        }
609    }
610
611    /// Get a `ValueBag` from a `str`.
612    #[inline]
613    pub const fn from_str(v: &'v str) -> ValueBag<'v> {
614        ValueBag {
615            inner: internal::Internal::Str(v),
616        }
617    }
618
619    /// Get a `ValueBag` from a `char`.
620    #[inline]
621    pub const fn from_char(v: char) -> ValueBag<'v> {
622        ValueBag {
623            inner: internal::Internal::Char(v),
624        }
625    }
626
627    /// Get a `ValueBag` from a reference to a `ValueBag`.
628    #[inline]
629    pub const fn by_ref(&self) -> ValueBag<'_> {
630        ValueBag {
631            inner: self.inner.by_ref(),
632        }
633    }
634}
635
636#[cfg(test)]
637mod tests {
638    use super::*;
639    use crate::std::mem;
640
641    #[cfg(feature = "inline-i128")]
642    const SIZE_LIMIT_U64: usize = 4;
643    #[cfg(not(feature = "inline-i128"))]
644    const SIZE_LIMIT_U64: usize = 3;
645
646    #[test]
647    fn value_bag_size() {
648        let size = mem::size_of::<ValueBag<'_>>();
649        let limit = mem::size_of::<u64>() * SIZE_LIMIT_U64;
650
651        if size > limit {
652            panic!(
653                "`ValueBag` size ({} bytes) is too large (expected up to {} bytes)",
654                size, limit,
655            );
656        }
657    }
658}