Skip to main content

value_bag/internal/
fmt.rs

1//! Integration between `Value` and `std::fmt`.
2//!
3//! This module allows any `Value` to implement the `Debug` and `Display` traits,
4//! and for any `Debug` or `Display` to be captured as a `Value`.
5
6use crate::{
7    fill::Slot,
8    std::{any::Any, fmt},
9    Error, ValueBag,
10};
11
12use super::{Internal, InternalVisitor};
13
14impl<'v> ValueBag<'v> {
15    /// Get a value from a debuggable type.
16    ///
17    /// This method will attempt to capture the given value as a well-known primitive
18    /// before resorting to using its `Debug` implementation.
19    pub fn capture_debug<T>(value: &'v T) -> Self
20    where
21        T: Debug + 'static,
22    {
23        Self::try_capture(value).unwrap_or(ValueBag {
24            inner: Internal::Debug(value),
25        })
26    }
27
28    /// Get a value from a displayable type.
29    ///
30    /// This method will attempt to capture the given value as a well-known primitive
31    /// before resorting to using its `Display` implementation.
32    pub fn capture_display<T>(value: &'v T) -> Self
33    where
34        T: Display + 'static,
35    {
36        Self::try_capture(value).unwrap_or(ValueBag {
37            inner: Internal::Display(value),
38        })
39    }
40
41    /// Get a value from a debuggable type without capturing support.
42    pub const fn from_debug<T>(value: &'v T) -> Self
43    where
44        T: Debug,
45    {
46        ValueBag {
47            inner: Internal::AnonDebug(value),
48        }
49    }
50
51    /// Get a value from a displayable type without capturing support.
52    pub const fn from_display<T>(value: &'v T) -> Self
53    where
54        T: Display,
55    {
56        ValueBag {
57            inner: Internal::AnonDisplay(value),
58        }
59    }
60
61    /// Get a value from a debuggable type without capturing support.
62    #[inline]
63    pub const fn from_dyn_debug(value: &'v dyn Debug) -> Self {
64        ValueBag {
65            inner: Internal::AnonDebug(value),
66        }
67    }
68
69    /// Get a value from a displayable type without capturing support.
70    #[inline]
71    pub const fn from_dyn_display(value: &'v dyn Display) -> Self {
72        ValueBag {
73            inner: Internal::AnonDisplay(value),
74        }
75    }
76}
77
78pub(crate) trait DowncastDisplay {
79    fn as_any(&self) -> &dyn Any;
80    fn as_super(&self) -> &dyn fmt::Display;
81}
82
83impl<T: fmt::Display + 'static> DowncastDisplay for T {
84    fn as_any(&self) -> &dyn Any {
85        self
86    }
87
88    fn as_super(&self) -> &dyn fmt::Display {
89        self
90    }
91}
92
93impl<'a> fmt::Display for dyn DowncastDisplay + Send + Sync + 'a {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        self.as_super().fmt(f)
96    }
97}
98
99pub(crate) trait DowncastDebug {
100    fn as_any(&self) -> &dyn Any;
101    fn as_super(&self) -> &dyn fmt::Debug;
102}
103
104impl<T: fmt::Debug + 'static> DowncastDebug for T {
105    fn as_any(&self) -> &dyn Any {
106        self
107    }
108
109    fn as_super(&self) -> &dyn fmt::Debug {
110        self
111    }
112}
113
114impl<'a> fmt::Debug for dyn DowncastDebug + Send + Sync + 'a {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        self.as_super().fmt(f)
117    }
118}
119
120impl<'s, 'f> Slot<'s, 'f> {
121    /// Fill the slot with a debuggable value.
122    ///
123    /// The given value doesn't need to satisfy any particular lifetime constraints.
124    pub fn fill_debug<T>(self, value: T) -> Result<(), Error>
125    where
126        T: Debug,
127    {
128        self.fill(|visitor| visitor.debug(&value))
129    }
130
131    /// Fill the slot with a displayable value.
132    ///
133    /// The given value doesn't need to satisfy any particular lifetime constraints.
134    pub fn fill_display<T>(self, value: T) -> Result<(), Error>
135    where
136        T: Display,
137    {
138        self.fill(|visitor| visitor.display(&value))
139    }
140}
141
142pub use self::fmt::{Debug, Display};
143
144impl<'v> Debug for ValueBag<'v> {
145    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146        struct DebugVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>);
147
148        impl<'a, 'b: 'a, 'v> InternalVisitor<'v> for DebugVisitor<'a, 'b> {
149            fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> {
150                v.fill(crate::fill::Slot::new(self))
151            }
152
153            fn debug(&mut self, v: &dyn Debug) -> Result<(), Error> {
154                Debug::fmt(v, self.0)?;
155
156                Ok(())
157            }
158
159            fn display(&mut self, v: &dyn Display) -> Result<(), Error> {
160                Display::fmt(v, self.0)?;
161
162                Ok(())
163            }
164
165            fn u64(&mut self, v: u64) -> Result<(), Error> {
166                Debug::fmt(&v, self.0)?;
167
168                Ok(())
169            }
170
171            fn i64(&mut self, v: i64) -> Result<(), Error> {
172                Debug::fmt(&v, self.0)?;
173
174                Ok(())
175            }
176
177            fn u128(&mut self, v: &u128) -> Result<(), Error> {
178                Debug::fmt(&v, self.0)?;
179
180                Ok(())
181            }
182
183            fn i128(&mut self, v: &i128) -> Result<(), Error> {
184                Debug::fmt(&v, self.0)?;
185
186                Ok(())
187            }
188
189            fn f64(&mut self, v: f64) -> Result<(), Error> {
190                Debug::fmt(&v, self.0)?;
191
192                Ok(())
193            }
194
195            fn bool(&mut self, v: bool) -> Result<(), Error> {
196                Debug::fmt(&v, self.0)?;
197
198                Ok(())
199            }
200
201            fn char(&mut self, v: char) -> Result<(), Error> {
202                Debug::fmt(&v, self.0)?;
203
204                Ok(())
205            }
206
207            fn str(&mut self, v: &str) -> Result<(), Error> {
208                Debug::fmt(&v, self.0)?;
209
210                Ok(())
211            }
212
213            fn none(&mut self) -> Result<(), Error> {
214                self.debug(&format_args!("None"))
215            }
216
217            #[cfg(feature = "error")]
218            fn error(&mut self, v: &(dyn std::error::Error + 'static)) -> Result<(), Error> {
219                Debug::fmt(v, self.0)?;
220
221                Ok(())
222            }
223
224            #[cfg(feature = "sval2")]
225            fn sval2(&mut self, v: &dyn crate::internal::sval::v2::Value) -> Result<(), Error> {
226                crate::internal::sval::v2::fmt(self.0, v)
227            }
228
229            #[cfg(feature = "serde1")]
230            fn serde1(
231                &mut self,
232                v: &dyn crate::internal::serde::v1::Serialize,
233            ) -> Result<(), Error> {
234                crate::internal::serde::v1::fmt(self.0, v)
235            }
236
237            #[cfg(feature = "seq")]
238            fn seq(&mut self, seq: &dyn crate::internal::seq::Seq) -> Result<(), Error> {
239                let mut visitor = seq::FmtSeq(self.0.debug_list());
240                seq.visit(&mut visitor);
241                visitor.0.finish()?;
242
243                Ok(())
244            }
245
246            fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> {
247                write!(self.0, "<{msg}>")?;
248
249                Ok(())
250            }
251        }
252
253        self.internal_visit(&mut DebugVisitor(f))
254            .map_err(|_| fmt::Error)?;
255
256        Ok(())
257    }
258}
259
260impl<'v> Display for ValueBag<'v> {
261    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262        struct DisplayVisitor<'a, 'b: 'a>(&'a mut fmt::Formatter<'b>);
263
264        impl<'a, 'b: 'a, 'v> InternalVisitor<'v> for DisplayVisitor<'a, 'b> {
265            fn fill(&mut self, v: &dyn crate::fill::Fill) -> Result<(), Error> {
266                v.fill(crate::fill::Slot::new(self))
267            }
268
269            fn debug(&mut self, v: &dyn Debug) -> Result<(), Error> {
270                Debug::fmt(v, self.0)?;
271
272                Ok(())
273            }
274
275            fn display(&mut self, v: &dyn Display) -> Result<(), Error> {
276                Display::fmt(v, self.0)?;
277
278                Ok(())
279            }
280
281            fn u64(&mut self, v: u64) -> Result<(), Error> {
282                Display::fmt(&v, self.0)?;
283
284                Ok(())
285            }
286
287            fn i64(&mut self, v: i64) -> Result<(), Error> {
288                Display::fmt(&v, self.0)?;
289
290                Ok(())
291            }
292
293            fn u128(&mut self, v: &u128) -> Result<(), Error> {
294                Display::fmt(&v, self.0)?;
295
296                Ok(())
297            }
298
299            fn i128(&mut self, v: &i128) -> Result<(), Error> {
300                Display::fmt(&v, self.0)?;
301
302                Ok(())
303            }
304
305            fn f64(&mut self, v: f64) -> Result<(), Error> {
306                Display::fmt(&v, self.0)?;
307
308                Ok(())
309            }
310
311            fn bool(&mut self, v: bool) -> Result<(), Error> {
312                Display::fmt(&v, self.0)?;
313
314                Ok(())
315            }
316
317            fn char(&mut self, v: char) -> Result<(), Error> {
318                Display::fmt(&v, self.0)?;
319
320                Ok(())
321            }
322
323            fn str(&mut self, v: &str) -> Result<(), Error> {
324                Display::fmt(&v, self.0)?;
325
326                Ok(())
327            }
328
329            fn none(&mut self) -> Result<(), Error> {
330                self.debug(&format_args!("None"))
331            }
332
333            #[cfg(feature = "error")]
334            fn error(&mut self, v: &(dyn std::error::Error + 'static)) -> Result<(), Error> {
335                Display::fmt(v, self.0)?;
336
337                Ok(())
338            }
339
340            #[cfg(feature = "sval2")]
341            fn sval2(&mut self, v: &dyn crate::internal::sval::v2::Value) -> Result<(), Error> {
342                crate::internal::sval::v2::fmt(self.0, v)
343            }
344
345            #[cfg(feature = "serde1")]
346            fn serde1(
347                &mut self,
348                v: &dyn crate::internal::serde::v1::Serialize,
349            ) -> Result<(), Error> {
350                crate::internal::serde::v1::fmt(self.0, v)
351            }
352
353            #[cfg(feature = "seq")]
354            fn seq(&mut self, seq: &dyn crate::internal::seq::Seq) -> Result<(), Error> {
355                let mut visitor = seq::FmtSeq(self.0.debug_list());
356                seq.visit(&mut visitor);
357                visitor.0.finish()?;
358
359                Ok(())
360            }
361
362            fn poisoned(&mut self, msg: &'static str) -> Result<(), Error> {
363                write!(self.0, "<{msg}>")?;
364
365                Ok(())
366            }
367        }
368
369        self.internal_visit(&mut DisplayVisitor(f))
370            .map_err(|_| fmt::Error)?;
371
372        Ok(())
373    }
374}
375
376#[cfg(feature = "seq")]
377mod seq {
378    use super::*;
379    use core::ops::ControlFlow;
380
381    pub(super) struct FmtSeq<'a, 'b>(pub(super) fmt::DebugList<'b, 'a>);
382
383    impl<'a, 'b, 'c> crate::internal::seq::Visitor<'c> for FmtSeq<'a, 'b> {
384        fn element(&mut self, inner: ValueBag) -> ControlFlow<()> {
385            self.0.entry(&inner);
386            ControlFlow::Continue(())
387        }
388    }
389}
390
391#[cfg(feature = "owned")]
392pub(crate) mod owned {
393    use crate::std::{boxed::Box, fmt};
394
395    #[cfg(feature = "inline-str")]
396    use crate::internal::owned::inline_str::{self, InlineStr};
397
398    impl fmt::Debug for crate::OwnedValueBag {
399        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
400            fmt::Debug::fmt(&self.by_ref(), f)
401        }
402    }
403
404    impl fmt::Display for crate::OwnedValueBag {
405        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
406            fmt::Display::fmt(&self.by_ref(), f)
407        }
408    }
409
410    #[cfg(not(feature = "inline-str"))]
411    pub(crate) enum InlineFmt {}
412
413    #[derive(Clone)]
414    pub(crate) struct OwnedFmt(Box<str>);
415
416    pub(crate) fn buffer_debug(v: impl fmt::Debug) -> Result<InlineFmt, OwnedFmt> {
417        #[cfg(feature = "inline-str")]
418        {
419            buffer_inline(format_args!("{v:?}"))
420        }
421        #[cfg(not(feature = "inline-str"))]
422        {
423            Err(OwnedFmt(format!("{v:?}").into()))
424        }
425    }
426
427    pub(crate) fn buffer_display(v: impl fmt::Display) -> Result<InlineFmt, OwnedFmt> {
428        #[cfg(feature = "inline-str")]
429        {
430            buffer_inline(v)
431        }
432        #[cfg(not(feature = "inline-str"))]
433        {
434            use crate::std::string::ToString as _;
435
436            Err(OwnedFmt(v.to_string().into()))
437        }
438    }
439
440    impl fmt::Debug for OwnedFmt {
441        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
442            fmt::Display::fmt(self, f)
443        }
444    }
445
446    impl fmt::Display for OwnedFmt {
447        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
448            fmt::Display::fmt(&self.0, f)
449        }
450    }
451
452    #[cfg(feature = "inline-str")]
453    #[inline]
454    fn buffer_inline(v: impl fmt::Display) -> Result<InlineFmt, OwnedFmt> {
455        // Use a slightly larger buffer in case we can compress the resulting buffer down to 22 bytes
456        // This can also save a few extra bounds checks and re-allocations
457        match InlineStr::<64>::buffer(v) {
458            Ok(inline) => {
459                if inline.len() <= inline_str::MAX_INLINE_LEN {
460                    // SAFETY: The condition above guarantees `inline` will fit in `MAX_INLINE_LEN` bytes
461                    let inline = unsafe {
462                        InlineStr::<{ inline_str::MAX_INLINE_LEN }>::copy_from_unchecked(
463                            inline.get(),
464                        )
465                    };
466
467                    Ok(InlineFmt(inline))
468                } else {
469                    Err(OwnedFmt(Box::from(inline.get())))
470                }
471            }
472            Err(spilled) => Err(OwnedFmt(spilled)),
473        }
474    }
475
476    #[cfg(feature = "inline-str")]
477    #[derive(Clone, Copy)]
478    pub(crate) struct InlineFmt(InlineStr<{ inline_str::MAX_INLINE_LEN }>);
479
480    #[cfg(feature = "inline-str")]
481    impl fmt::Debug for InlineFmt {
482        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
483            fmt::Display::fmt(self, f)
484        }
485    }
486
487    #[cfg(feature = "inline-str")]
488    impl fmt::Display for InlineFmt {
489        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
490            fmt::Display::fmt(&self.0, f)
491        }
492    }
493}
494
495impl<'v> From<&'v dyn Debug> for ValueBag<'v> {
496    #[inline]
497    fn from(v: &'v dyn Debug) -> Self {
498        ValueBag::from_dyn_debug(v)
499    }
500}
501
502impl<'v> From<Option<&'v dyn Debug>> for ValueBag<'v> {
503    #[inline]
504    fn from(v: Option<&'v dyn Debug>) -> Self {
505        ValueBag::from_option(v)
506    }
507}
508
509impl<'v, 'u> From<&'v &'u dyn Debug> for ValueBag<'v>
510where
511    'u: 'v,
512{
513    #[inline]
514    fn from(v: &'v &'u dyn Debug) -> Self {
515        ValueBag::from_dyn_debug(*v)
516    }
517}
518
519impl<'v> From<&'v dyn Display> for ValueBag<'v> {
520    #[inline]
521    fn from(v: &'v dyn Display) -> Self {
522        ValueBag::from_dyn_display(v)
523    }
524}
525
526impl<'v> From<Option<&'v dyn Display>> for ValueBag<'v> {
527    #[inline]
528    fn from(v: Option<&'v dyn Display>) -> Self {
529        ValueBag::from_option(v)
530    }
531}
532
533impl<'v, 'u> From<&'v &'u dyn Display> for ValueBag<'v>
534where
535    'u: 'v,
536{
537    #[inline]
538    fn from(v: &'v &'u dyn Display) -> Self {
539        ValueBag::from_dyn_display(*v)
540    }
541}
542
543#[cfg(test)]
544mod tests {
545    #[cfg(target_arch = "wasm32")]
546    use wasm_bindgen_test::*;
547
548    use super::*;
549    use crate::{
550        std::string::ToString,
551        test::{IntoValueBag, TestToken},
552    };
553
554    #[test]
555    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
556    fn fmt_capture() {
557        assert_eq!(
558            ValueBag::capture_debug(&1u16).to_test_token(),
559            TestToken::U64(1)
560        );
561        assert_eq!(
562            ValueBag::capture_display(&1u16).to_test_token(),
563            TestToken::U64(1)
564        );
565
566        assert_eq!(
567            ValueBag::capture_debug(&Some(1u16)).to_test_token(),
568            TestToken::U64(1)
569        );
570    }
571
572    #[test]
573    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
574    fn fmt_fill() {
575        assert_eq!(
576            ValueBag::from_fill(&|slot: Slot| slot.fill_debug(1u16)).to_test_token(),
577            TestToken::Str("1".into())
578        );
579        assert_eq!(
580            ValueBag::from_fill(&|slot: Slot| slot.fill_display(1u16)).to_test_token(),
581            TestToken::Str("1".into())
582        );
583    }
584
585    #[test]
586    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
587    fn fmt_capture_args() {
588        assert_eq!(
589            ValueBag::from_debug(&format_args!("a {}", "value")).to_string(),
590            "a value"
591        );
592    }
593
594    #[test]
595    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
596    fn fmt_cast() {
597        assert_eq!(
598            42u64,
599            ValueBag::capture_debug(&42u64)
600                .to_u64()
601                .expect("invalid value")
602        );
603
604        assert_eq!(
605            "a string",
606            ValueBag::capture_display(&"a string")
607                .to_borrowed_str()
608                .expect("invalid value")
609        );
610    }
611
612    #[test]
613    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
614    fn fmt_downcast() {
615        #[derive(Debug, PartialEq, Eq)]
616        struct Timestamp(usize);
617
618        impl Display for Timestamp {
619            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
620                write!(f, "time is {}", self.0)
621            }
622        }
623
624        let ts = Timestamp(42);
625
626        assert_eq!(
627            &ts,
628            ValueBag::capture_debug(&ts)
629                .downcast_ref::<Timestamp>()
630                .expect("invalid value")
631        );
632
633        assert_eq!(
634            &ts,
635            ValueBag::capture_display(&ts)
636                .downcast_ref::<Timestamp>()
637                .expect("invalid value")
638        );
639    }
640
641    #[test]
642    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
643    fn fmt_debug() {
644        assert_eq!(
645            format!("{:?}", "a string"),
646            format!("{:?}", "a string".into_value_bag().by_ref()),
647        );
648
649        assert_eq!(
650            format!("{:04?}", 42u64),
651            format!("{:04?}", 42u64.into_value_bag().by_ref()),
652        );
653    }
654
655    #[test]
656    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
657    fn fmt_display() {
658        assert_eq!(
659            format!("{}", "a string"),
660            format!("{}", "a string".into_value_bag().by_ref()),
661        );
662
663        assert_eq!(
664            format!("{:04}", 42u64),
665            format!("{:04}", 42u64.into_value_bag().by_ref()),
666        );
667    }
668
669    #[cfg(feature = "seq")]
670    mod seq_support {
671        use super::*;
672
673        #[test]
674        #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
675        fn fmt_debug_seq() {
676            assert_eq!(
677                "[01, 02, 03]",
678                format!("{:>02?}", ValueBag::from_seq_slice(&[1, 2, 3]))
679            );
680        }
681
682        #[test]
683        #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
684        fn fmt_display_seq() {
685            assert_eq!(
686                "[1, 2, 3]",
687                format!("{}", ValueBag::from_seq_slice(&[1, 2, 3]))
688            );
689        }
690    }
691}