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.11.1" )] |
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 | |
21 | This library internally ties several frameworks together. The details of how |
22 | this 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 | └───────────────┼────────────────┘ |
39 | │ |
40 | ┌───────────────────────▼────────────────────────┐ |
41 | │ValueBag::capture_sval2│ValueBag::capture_serde1│ 5. ctors on `ValueBag` |
42 | └───────────────────────┼────────────────────────┘ |
43 | │ |
44 | ┌───────────────────────▼───────────────────────────┐ |
45 | │impl Value for ValueBag│impl Serialize for ValueBag│ 6. trait impls on `ValueBag` |
46 | └───────────────────────┴───────────────────────────┘ |
47 | |
48 | ## 1. libs on crates.io |
49 | |
50 | These are the frameworks like `serde` or `sval`. |
51 | |
52 | ## 2. meta crates with features |
53 | |
54 | These are crates that are internal to `value-bag`. They depend on the public |
55 | framework and any utility crates that come along with it. They also expose |
56 | features for any other framework. This is done this way so `value-bag` can use |
57 | Cargo's `crate?/feature` syntax to conditionally add framework support. |
58 | |
59 | ## 3. internal modules with `InternalVisitor` |
60 | |
61 | These are modules in `value-bag` that integrate the framework using the |
62 | `InternalVisitor` trait. This makes it possible for that framework to cast |
63 | primitive values and pass-through any other framework. |
64 | |
65 | ## 4. variants in `Internal` enum |
66 | |
67 | These are individual variants on the `Internal` enum that the `ValueBag` |
68 | type wraps. Each framework has one or more variants in this enum. |
69 | |
70 | ## 5. ctors on `ValueBag` |
71 | |
72 | These are constructors for producers of `ValueBag`s that accept a value |
73 | implementing a serialization trait from a specific framework, like |
74 | `serde::Serialize` or `sval::Value`. |
75 | |
76 | ## 7. trait impls on `ValueBag` |
77 | |
78 | These are trait impls for consumers of `ValueBag`s that serialize the |
79 | underlying 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)] |
85 | extern crate std; |
86 | |
87 | #[cfg (all(not(test), feature = "alloc" , not(feature = "std" )))] |
88 | #[macro_use ] |
89 | #[allow (unused_imports)] |
90 | extern crate core; |
91 | |
92 | #[cfg (all(not(test), feature = "alloc" , not(feature = "std" )))] |
93 | #[macro_use ] |
94 | #[allow (unused_imports)] |
95 | extern crate alloc; |
96 | |
97 | #[cfg (all(not(test), feature = "alloc" , not(feature = "std" )))] |
98 | #[allow (unused_imports)] |
99 | mod 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)] |
112 | extern crate core as std; |
113 | |
114 | mod error; |
115 | pub mod fill; |
116 | mod impls; |
117 | mod internal; |
118 | pub mod visit; |
119 | |
120 | #[cfg (any(test, feature = "test" ))] |
121 | pub mod test; |
122 | |
123 | #[cfg (feature = "owned" )] |
124 | mod owned; |
125 | #[cfg (feature = "owned" )] |
126 | pub use self::owned::*; |
127 | |
128 | pub 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(×tamp); |
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)] |
443 | pub struct ValueBag<'v> { |
444 | inner: internal::Internal<'v>, |
445 | } |
446 | |
447 | impl<'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)] |
637 | mod 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 | } |
659 | |