radix_rust/contextual_display.rs
1use crate::rust::fmt;
2use crate::rust::prelude::*;
3
4/// This trait is used where context is required to correctly display a value.
5///
6/// Typically, this is due to needing to know the current network to display addresses.
7/// Other forms of Context are also possible. See `ComponentAddress`
8/// or `TransactionReceipt` in the `radix-engine` crate for example implementations.
9///
10/// The `Context` used should typically just be a wrapper type around references, and so
11/// be a small, cheap, ephemeral value on the stack (if it's not just optimized away entirely).
12/// It is therefore recommended that the `Context` implement `Copy`,
13/// to make it very easy to pass around and re-use.
14///
15pub trait ContextualDisplay<Context> {
16 type Error;
17
18 /// Formats the value to the given `fmt::Write` buffer, making use of the provided context.
19 /// See also [`format`], which is typically easier to use, as it takes an `Into<Context>`
20 /// instead of a `&Context`.
21 ///
22 /// [`format`]: #method.format
23 fn contextual_format(
24 &self,
25 f: &mut fmt::Formatter,
26 context: &Context,
27 ) -> Result<(), Self::Error>;
28
29 /// Formats the value to the given `fmt::Write` buffer, making use of the provided context.
30 /// See also [`contextual_format`], which takes a `&Context` instead of an `Into<Context>`.
31 ///
32 /// Alternatively, the [`display`] method can be used to create an object that can be used
33 /// directly in a `format!` style macro.
34 ///
35 /// [`contextual_format`]: #method.contextual_format
36 /// [`display`]: #method.display
37 fn format<TContext: Into<Context>>(
38 &self,
39 f: &mut fmt::Formatter,
40 context: TContext,
41 ) -> Result<(), Self::Error> {
42 self.contextual_format(f, &context.into())
43 }
44
45 /// Returns an object implementing `fmt::Display`, which can be used in a `format!` style macro.
46 ///
47 /// Whilst this is syntactically nicer, beware that the use of `format!` absorbs any errors during
48 /// formatting, replacing them with `fmt::Error`.
49 /// If you'd like to preserve errors, use the [`format`] method instead. This may require manually
50 /// splitting up your `format!` style macro. For example:
51 ///
52 /// ```rust,ignore
53 /// // Syntactically nice, but the AddressError is swallowed into fmt::Error
54 /// write!(f, "ComponentAddress(\"{}\")", address.display(context))?;
55 ///
56 /// // Less nice, but the AddressError is correctly returned
57 /// f.write_str("ComponentAddress(\"")?;
58 /// address.format(f, context)?;
59 /// f.write_str("\")")?;
60 /// ```
61 ///
62 /// [`format`]: #method.format
63 fn display<'a, TContext: Into<Context>>(
64 &'a self,
65 context: TContext,
66 ) -> ContextDisplayable<'a, Self, Context> {
67 ContextDisplayable {
68 value: self,
69 context: context.into(),
70 }
71 }
72
73 /// Returns an object implementing [`fmt::Debug`] using the contextual display implementation.
74 ///
75 /// Typically you should use [`format`] instead.
76 ///
77 /// [`format`]: #method.format
78 fn debug_as_display<'a, TContext: Into<Context>>(
79 &'a self,
80 context: TContext,
81 ) -> ContextDebuggableAsDisplay<'a, Self, Context> {
82 ContextDebuggableAsDisplay {
83 value: self,
84 context: context.into(),
85 }
86 }
87
88 fn to_string<TContext: Into<Context>>(&self, context: TContext) -> String {
89 self.display(context).to_string()
90 }
91}
92
93pub struct ContextDisplayable<'a, TValue, TContext>
94where
95 TValue: ContextualDisplay<TContext> + ?Sized,
96{
97 value: &'a TValue,
98 context: TContext,
99}
100
101impl<'a, TValue, TContext> fmt::Display for ContextDisplayable<'a, TValue, TContext>
102where
103 TValue: ContextualDisplay<TContext> + ?Sized,
104{
105 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
106 self.value
107 .contextual_format(f, &self.context)
108 .map_err(|_| fmt::Error) // We eat any errors into fmt::Error
109 }
110}
111
112pub struct ContextDebuggableAsDisplay<'a, TValue, TContext>
113where
114 TValue: ContextualDisplay<TContext> + ?Sized,
115{
116 value: &'a TValue,
117 context: TContext,
118}
119
120impl<'a, TValue, TContext> fmt::Debug for ContextDebuggableAsDisplay<'a, TValue, TContext>
121where
122 TValue: ContextualDisplay<TContext> + ?Sized,
123{
124 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
125 self.value
126 .contextual_format(f, &self.context)
127 .map_err(|_| fmt::Error) // We eat any errors into fmt::Error
128 }
129}