value_bag/internal/cast/
mod.rs

1//! Coerce a `Value` into some concrete types.
2//!
3//! These operations are cheap when the captured value is a simple primitive,
4//! but may end up executing arbitrary caller code if the value is complex.
5//! They will also attempt to downcast erased types into a primitive where possible.
6
7use crate::std::fmt;
8
9#[cfg(feature = "alloc")]
10use crate::std::{borrow::ToOwned, string::String};
11
12use super::{Internal, InternalVisitor};
13use crate::{Error, ValueBag};
14
15mod primitive;
16
17impl ValueBag<'static> {
18    /// Try capture an owned raw value.
19    ///
20    /// This method will return `Some` if the value is a simple primitive
21    /// that can be captured without losing its structure. In other cases
22    /// this method will return `None`.
23    #[cfg(feature = "owned")]
24    pub fn try_capture_owned<T>(value: &'_ T) -> Option<Self>
25    where
26        T: ?Sized + 'static,
27    {
28        primitive::from_owned_any(value)
29    }
30}
31
32impl<'v> ValueBag<'v> {
33    /// Try capture a raw value.
34    ///
35    /// This method will return `Some` if the value is a simple primitive
36    /// that can be captured without losing its structure. In other cases
37    /// this method will return `None`.
38    pub fn try_capture<T>(value: &'v T) -> Option<Self>
39    where
40        T: ?Sized + 'static,
41    {
42        primitive::from_any(value)
43    }
44
45    /// Try get a `u64` from this value.
46    ///
47    /// This method is cheap for primitive types, but may call arbitrary
48    /// serialization implementations for complex ones.
49    pub fn to_u64(&self) -> Option<u64> {
50        self.inner.cast().into_u64()
51    }
52
53    /// Try get a `i64` from this value.
54    ///
55    /// This method is cheap for primitive types, but may call arbitrary
56    /// serialization implementations for complex ones.
57    pub fn to_i64(&self) -> Option<i64> {
58        self.inner.cast().into_i64()
59    }
60
61    /// Try get a `u128` from this value.
62    ///
63    /// This method is cheap for primitive types, but may call arbitrary
64    /// serialization implementations for complex ones.
65    pub fn to_u128(&self) -> Option<u128> {
66        self.inner.cast().into_u128()
67    }
68
69    /// Try get a `i128` from this value.
70    ///
71    /// This method is cheap for primitive types, but may call arbitrary
72    /// serialization implementations for complex ones.
73    pub fn to_i128(&self) -> Option<i128> {
74        self.inner.cast().into_i128()
75    }
76
77    /// Try get a `f64` from this value.
78    ///
79    /// This method is cheap for primitive types, but may call arbitrary
80    /// serialization implementations for complex ones.
81    ///
82    /// This method is based on standard `TryInto` conversions, and will
83    /// only return `Some` if there's a guaranteed lossless conversion between
84    /// the source and destination types. For a more lenient alternative, see
85    /// [`ValueBag::as_f64`].
86    pub fn to_f64(&self) -> Option<f64> {
87        self.inner.cast().into_f64()
88    }
89
90    /// Get a `f64` from this value.
91    ///
92    /// This method is cheap for primitive types, but may call arbitrary
93    /// serialization implementations for complex ones.
94    ///
95    /// This method is like [`ValueBag::to_f64`] except will always return
96    /// a `f64`, regardless of the actual type of underlying value. For
97    /// numeric types, it will use a regular `as` conversion, which may be lossy.
98    /// For non-numeric types it will return `NaN`.
99    pub fn as_f64(&self) -> f64 {
100        self.inner.cast().as_f64()
101    }
102
103    /// Try get a `bool` from this value.
104    ///
105    /// This method is cheap for primitive types, but may call arbitrary
106    /// serialization implementations for complex ones.
107    pub fn to_bool(&self) -> Option<bool> {
108        self.inner.cast().into_bool()
109    }
110
111    /// Try get a `char` from this value.
112    ///
113    /// This method is cheap for primitive types, but may call arbitrary
114    /// serialization implementations for complex ones.
115    pub fn to_char(&self) -> Option<char> {
116        self.inner.cast().into_char()
117    }
118
119    /// Try get a `str` from this value.
120    ///
121    /// This method is cheap for primitive types. It won't allocate an owned
122    /// `String` if the value is a complex type.
123    pub fn to_borrowed_str(&self) -> Option<&'v str> {
124        self.inner.cast().into_borrowed_str()
125    }
126
127    /// Check whether this value is empty.
128    pub fn is_empty(&self) -> bool {
129        matches!(self.inner, Internal::None)
130    }
131
132    /// Check whether this value can be downcast to `T`.
133    pub fn is<T: 'static>(&self) -> bool {
134        self.downcast_ref::<T>().is_some()
135    }
136
137    /// Try downcast this value to `T`.
138    pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
139        match self.inner {
140            Internal::Debug(value) => value.as_any().downcast_ref(),
141            Internal::Display(value) => value.as_any().downcast_ref(),
142            #[cfg(feature = "error")]
143            Internal::Error(value) => value.as_any().downcast_ref(),
144            #[cfg(feature = "sval2")]
145            Internal::Sval2(value) => value.as_any().downcast_ref(),
146            #[cfg(feature = "serde1")]
147            Internal::Serde1(value) => value.as_any().downcast_ref(),
148
149            #[cfg(feature = "owned")]
150            Internal::SharedDebug(ref value) => value.as_any().downcast_ref(),
151            #[cfg(feature = "owned")]
152            Internal::SharedDisplay(ref value) => value.as_any().downcast_ref(),
153            #[cfg(all(feature = "error", feature = "owned"))]
154            Internal::SharedError(ref value) => value.as_any().downcast_ref(),
155            #[cfg(all(feature = "serde1", feature = "owned"))]
156            Internal::SharedSerde1(ref value) => value.as_any().downcast_ref(),
157            #[cfg(all(feature = "sval2", feature = "owned"))]
158            Internal::SharedSval2(ref value) => value.as_any().downcast_ref(),
159            #[cfg(all(feature = "seq", feature = "owned"))]
160            Internal::SharedSeq(ref value) => value.as_any().downcast_ref(),
161
162            #[cfg(feature = "owned")]
163            Internal::SharedRefDebug(value) => value.as_any().downcast_ref(),
164            #[cfg(feature = "owned")]
165            Internal::SharedRefDisplay(value) => value.as_any().downcast_ref(),
166            #[cfg(all(feature = "error", feature = "owned"))]
167            Internal::SharedRefError(value) => value.as_any().downcast_ref(),
168            #[cfg(all(feature = "serde1", feature = "owned"))]
169            Internal::SharedRefSerde1(value) => value.as_any().downcast_ref(),
170            #[cfg(all(feature = "sval2", feature = "owned"))]
171            Internal::SharedRefSval2(value) => value.as_any().downcast_ref(),
172            #[cfg(all(feature = "seq", feature = "owned"))]
173            Internal::SharedRefSeq(value) => value.as_any().downcast_ref(),
174
175            _ => None,
176        }
177    }
178}
179
180impl<'v> Internal<'v> {
181    /// Cast the inner value to another type.
182    #[inline]
183    fn cast(&self) -> Cast<'v> {
184        struct CastVisitor<'v>(Cast<'v>);
185
186        impl<'v> InternalVisitor<'v> for CastVisitor<'v> {
187            #[inline]
188            fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> {
189                v.fill(crate::fill::Slot::new(self))
190            }
191
192            #[inline]
193            fn debug(&mut self, _: &dyn fmt::Debug) -> Result<(), Error> {
194                Ok(())
195            }
196
197            #[inline]
198            fn display(&mut self, _: &dyn fmt::Display) -> Result<(), Error> {
199                Ok(())
200            }
201
202            #[inline]
203            fn u64(&mut self, v: u64) -> Result<(), Error> {
204                self.0 = Cast::Unsigned(v);
205                Ok(())
206            }
207
208            #[inline]
209            fn i64(&mut self, v: i64) -> Result<(), Error> {
210                self.0 = Cast::Signed(v);
211                Ok(())
212            }
213
214            #[inline]
215            fn u128(&mut self, v: &u128) -> Result<(), Error> {
216                self.0 = Cast::BigUnsigned(*v);
217                Ok(())
218            }
219
220            #[inline]
221            fn i128(&mut self, v: &i128) -> Result<(), Error> {
222                self.0 = Cast::BigSigned(*v);
223                Ok(())
224            }
225
226            #[inline]
227            fn f64(&mut self, v: f64) -> Result<(), Error> {
228                self.0 = Cast::Float(v);
229                Ok(())
230            }
231
232            #[inline]
233            fn bool(&mut self, v: bool) -> Result<(), Error> {
234                self.0 = Cast::Bool(v);
235                Ok(())
236            }
237
238            #[inline]
239            fn char(&mut self, v: char) -> Result<(), Error> {
240                self.0 = Cast::Char(v);
241                Ok(())
242            }
243
244            #[cfg(feature = "alloc")]
245            #[inline]
246            fn str(&mut self, s: &str) -> Result<(), Error> {
247                self.0 = Cast::String(s.to_owned());
248                Ok(())
249            }
250
251            #[cfg(not(feature = "alloc"))]
252            #[inline]
253            fn str(&mut self, _: &str) -> Result<(), Error> {
254                Ok(())
255            }
256
257            #[inline]
258            fn borrowed_str(&mut self, v: &'v str) -> Result<(), Error> {
259                self.0 = Cast::Str(v);
260                Ok(())
261            }
262
263            #[inline]
264            fn none(&mut self) -> Result<(), Error> {
265                self.0 = Cast::None;
266                Ok(())
267            }
268
269            #[cfg(feature = "error")]
270            #[inline]
271            fn error(&mut self, _: &dyn super::error::Error) -> Result<(), Error> {
272                Ok(())
273            }
274
275            #[cfg(feature = "sval2")]
276            #[inline]
277            fn sval2(&mut self, v: &dyn super::sval::v2::Value) -> Result<(), Error> {
278                if super::sval::v2::internal_visit(v, self) {
279                    Ok(())
280                } else {
281                    Err(Error::msg("invalid cast"))
282                }
283            }
284
285            #[cfg(feature = "sval2")]
286            fn borrowed_sval2(&mut self, v: &'v dyn super::sval::v2::Value) -> Result<(), Error> {
287                if super::sval::v2::borrowed_internal_visit(v, self) {
288                    Ok(())
289                } else {
290                    Err(Error::msg("invalid cast"))
291                }
292            }
293
294            #[cfg(feature = "serde1")]
295            #[inline]
296            fn serde1(&mut self, v: &dyn super::serde::v1::Serialize) -> Result<(), Error> {
297                if super::serde::v1::internal_visit(v, self) {
298                    Ok(())
299                } else {
300                    Err(Error::msg("invalid cast"))
301                }
302            }
303
304            #[cfg(feature = "seq")]
305            fn seq(&mut self, _: &dyn super::seq::Seq) -> Result<(), Error> {
306                self.0 = Cast::None;
307                Ok(())
308            }
309
310            fn poisoned(&mut self, _: &'static str) -> Result<(), Error> {
311                self.0 = Cast::None;
312                Ok(())
313            }
314        }
315
316        match &self {
317            Internal::Signed(value) => Cast::Signed(*value),
318            Internal::Unsigned(value) => Cast::Unsigned(*value),
319            #[cfg(feature = "inline-i128")]
320            Internal::BigSigned(value) => Cast::BigSigned(*value),
321            #[cfg(not(feature = "inline-i128"))]
322            Internal::BigSigned(value) => Cast::BigSigned(**value),
323            #[cfg(feature = "inline-i128")]
324            Internal::BigUnsigned(value) => Cast::BigUnsigned(*value),
325            #[cfg(not(feature = "inline-i128"))]
326            Internal::BigUnsigned(value) => Cast::BigUnsigned(**value),
327            Internal::Float(value) => Cast::Float(*value),
328            Internal::Bool(value) => Cast::Bool(*value),
329            Internal::Char(value) => Cast::Char(*value),
330            Internal::Str(value) => Cast::Str(value),
331            Internal::None => Cast::None,
332            other => {
333                // If the erased value isn't a primitive then we visit it
334                let mut cast = CastVisitor(Cast::None);
335                let _ = other.internal_visit(&mut cast);
336                cast.0
337            }
338        }
339    }
340}
341
342pub(in crate::internal) enum Cast<'v> {
343    Signed(i64),
344    Unsigned(u64),
345    BigSigned(i128),
346    BigUnsigned(u128),
347    Float(f64),
348    Bool(bool),
349    Char(char),
350    Str(&'v str),
351    None,
352    #[cfg(feature = "alloc")]
353    String(String),
354}
355
356impl<'v> Cast<'v> {
357    #[inline]
358    fn into_borrowed_str(self) -> Option<&'v str> {
359        if let Cast::Str(value) = self {
360            Some(value)
361        } else {
362            None
363        }
364    }
365
366    #[inline]
367    fn into_u64(self) -> Option<u64> {
368        match self {
369            Cast::Unsigned(value) => Some(value),
370            Cast::BigUnsigned(value) => value.try_into().ok(),
371            Cast::Signed(value) => value.try_into().ok(),
372            Cast::BigSigned(value) => value.try_into().ok(),
373            _ => None,
374        }
375    }
376
377    #[inline]
378    fn into_i64(self) -> Option<i64> {
379        match self {
380            Cast::Signed(value) => Some(value),
381            Cast::BigSigned(value) => value.try_into().ok(),
382            Cast::Unsigned(value) => value.try_into().ok(),
383            Cast::BigUnsigned(value) => value.try_into().ok(),
384            _ => None,
385        }
386    }
387
388    #[inline]
389    fn into_u128(self) -> Option<u128> {
390        match self {
391            Cast::BigUnsigned(value) => Some(value),
392            Cast::Unsigned(value) => Some(value.into()),
393            Cast::Signed(value) => value.try_into().ok(),
394            Cast::BigSigned(value) => value.try_into().ok(),
395            _ => None,
396        }
397    }
398
399    #[inline]
400    fn into_i128(self) -> Option<i128> {
401        match self {
402            Cast::BigSigned(value) => Some(value),
403            Cast::Signed(value) => Some(value.into()),
404            Cast::Unsigned(value) => value.try_into().ok(),
405            Cast::BigUnsigned(value) => value.try_into().ok(),
406            _ => None,
407        }
408    }
409
410    #[inline]
411    fn into_f64(self) -> Option<f64> {
412        match self {
413            Cast::Float(value) => Some(value),
414            Cast::Unsigned(value) => u32::try_from(value)
415                .ok()
416                .and_then(|value| value.try_into().ok()),
417            Cast::Signed(value) => i32::try_from(value)
418                .ok()
419                .and_then(|value| value.try_into().ok()),
420            Cast::BigUnsigned(value) => u32::try_from(value)
421                .ok()
422                .and_then(|value| value.try_into().ok()),
423            Cast::BigSigned(value) => i32::try_from(value)
424                .ok()
425                .and_then(|value| value.try_into().ok()),
426            _ => None,
427        }
428    }
429
430    #[inline]
431    fn as_f64(self) -> f64 {
432        match self {
433            Cast::Float(value) => value,
434            Cast::Unsigned(value) => value as f64,
435            Cast::Signed(value) => value as f64,
436            Cast::BigUnsigned(value) => value as f64,
437            Cast::BigSigned(value) => value as f64,
438            _ => f64::NAN,
439        }
440    }
441
442    #[inline]
443    fn into_char(self) -> Option<char> {
444        if let Cast::Char(value) = self {
445            Some(value)
446        } else {
447            None
448        }
449    }
450
451    #[inline]
452    fn into_bool(self) -> Option<bool> {
453        if let Cast::Bool(value) = self {
454            Some(value)
455        } else {
456            None
457        }
458    }
459}
460
461#[cfg(feature = "alloc")]
462mod alloc_support {
463    use super::*;
464
465    use crate::std::borrow::Cow;
466
467    impl<'v> ValueBag<'v> {
468        /// Try get a `str` from this value.
469        ///
470        /// This method is cheap for primitive types, but may call arbitrary
471        /// serialization implementations for complex ones. If the serialization
472        /// implementation produces a short lived string it will be allocated.
473        #[inline]
474        pub fn to_str(&self) -> Option<Cow<'v, str>> {
475            self.inner.cast().into_str()
476        }
477    }
478
479    impl<'v> Cast<'v> {
480        #[inline]
481        pub(in crate::internal) fn into_str(self) -> Option<Cow<'v, str>> {
482            match self {
483                Cast::Str(value) => Some(value.into()),
484                Cast::String(value) => Some(value.into()),
485                _ => None,
486            }
487        }
488    }
489
490    #[cfg(test)]
491    mod tests {
492        #[cfg(target_arch = "wasm32")]
493        use wasm_bindgen_test::*;
494
495        use crate::{std::borrow::ToOwned, test::IntoValueBag, ValueBag};
496
497        #[test]
498        #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
499        fn primitive_cast() {
500            let short_lived = "a string".to_owned();
501            assert_eq!(
502                "a string",
503                (&*short_lived)
504                    .into_value_bag()
505                    .to_borrowed_str()
506                    .expect("invalid value")
507            );
508            assert_eq!(
509                "a string",
510                &*"a string".into_value_bag().to_str().expect("invalid value")
511            );
512            assert_eq!(
513                "a string",
514                (&*short_lived)
515                    .into_value_bag()
516                    .to_borrowed_str()
517                    .expect("invalid value")
518            );
519            assert_eq!(
520                "a string",
521                ValueBag::try_capture(&short_lived)
522                    .expect("invalid value")
523                    .to_borrowed_str()
524                    .expect("invalid value")
525            );
526        }
527    }
528}
529
530#[cfg(test)]
531mod tests {
532    #[cfg(target_arch = "wasm32")]
533    use wasm_bindgen_test::*;
534
535    use super::*;
536
537    use crate::test::IntoValueBag;
538
539    #[test]
540    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
541    fn is_empty() {
542        assert!(ValueBag::from(None::<i32>).is_empty(),);
543
544        assert!(ValueBag::try_capture(&None::<i32>).unwrap().is_empty(),);
545    }
546
547    #[test]
548    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
549    fn primitive_capture_str() {
550        let s: &str = "short lived";
551        assert_eq!(
552            "short lived",
553            ValueBag::try_capture(s)
554                .unwrap()
555                .to_borrowed_str()
556                .expect("invalid value")
557        );
558    }
559
560    #[test]
561    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
562    fn primitive_cast() {
563        assert_eq!(
564            "a string",
565            "a string"
566                .into_value_bag()
567                .by_ref()
568                .to_borrowed_str()
569                .expect("invalid value")
570        );
571
572        assert_eq!(
573            1u64,
574            1u8.into_value_bag()
575                .by_ref()
576                .to_u64()
577                .expect("invalid value")
578        );
579        assert_eq!(
580            1u64,
581            1u16.into_value_bag()
582                .by_ref()
583                .to_u64()
584                .expect("invalid value")
585        );
586        assert_eq!(
587            1u64,
588            1u32.into_value_bag()
589                .by_ref()
590                .to_u64()
591                .expect("invalid value")
592        );
593        assert_eq!(
594            1u64,
595            1u64.into_value_bag()
596                .by_ref()
597                .to_u64()
598                .expect("invalid value")
599        );
600        assert_eq!(
601            1u64,
602            1usize
603                .into_value_bag()
604                .by_ref()
605                .to_u64()
606                .expect("invalid value")
607        );
608        assert_eq!(
609            1u128,
610            1u128
611                .into_value_bag()
612                .by_ref()
613                .to_u128()
614                .expect("invalid value")
615        );
616
617        assert_eq!(
618            -1i64,
619            -(1i8
620                .into_value_bag()
621                .by_ref()
622                .to_i64()
623                .expect("invalid value"))
624        );
625        assert_eq!(
626            -1i64,
627            -(1i8
628                .into_value_bag()
629                .by_ref()
630                .to_i64()
631                .expect("invalid value"))
632        );
633        assert_eq!(
634            -1i64,
635            -(1i8
636                .into_value_bag()
637                .by_ref()
638                .to_i64()
639                .expect("invalid value"))
640        );
641        assert_eq!(
642            -1i64,
643            -(1i64
644                .into_value_bag()
645                .by_ref()
646                .to_i64()
647                .expect("invalid value"))
648        );
649        assert_eq!(
650            -1i64,
651            -(1isize
652                .into_value_bag()
653                .by_ref()
654                .to_i64()
655                .expect("invalid value"))
656        );
657        assert_eq!(
658            -1i128,
659            -(1i128
660                .into_value_bag()
661                .by_ref()
662                .to_i128()
663                .expect("invalid value"))
664        );
665
666        assert!(1f64.into_value_bag().by_ref().to_f64().is_some());
667        assert!(1u64.into_value_bag().by_ref().to_f64().is_some());
668        assert!((-1i64).into_value_bag().by_ref().to_f64().is_some());
669        assert!(1u128.into_value_bag().by_ref().to_f64().is_some());
670        assert!((-1i128).into_value_bag().by_ref().to_f64().is_some());
671
672        assert!(u64::MAX.into_value_bag().by_ref().to_u128().is_some());
673        assert!(i64::MIN.into_value_bag().by_ref().to_i128().is_some());
674        assert!(i64::MAX.into_value_bag().by_ref().to_u64().is_some());
675
676        assert!((-1i64).into_value_bag().by_ref().to_u64().is_none());
677        assert!(u64::MAX.into_value_bag().by_ref().to_i64().is_none());
678        assert!(u64::MAX.into_value_bag().by_ref().to_f64().is_none());
679
680        assert!(i128::MAX.into_value_bag().by_ref().to_i64().is_none());
681        assert!(u128::MAX.into_value_bag().by_ref().to_u64().is_none());
682
683        assert!(1f64.into_value_bag().by_ref().to_u64().is_none());
684
685        assert_eq!(
686            'a',
687            'a'.into_value_bag()
688                .by_ref()
689                .to_char()
690                .expect("invalid value")
691        );
692        assert!(true
693            .into_value_bag()
694            .by_ref()
695            .to_bool()
696            .expect("invalid value"));
697    }
698
699    #[test]
700    fn as_cast() {
701        assert_eq!(1.0, 1f64.into_value_bag().as_f64());
702        assert_eq!(1.0, 1u64.into_value_bag().as_f64());
703        assert_eq!(-1.0, -(1i64.into_value_bag().as_f64()));
704        assert!(true.into_value_bag().as_f64().is_nan());
705    }
706}