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<F: fmt::Write>(
24        &self,
25        f: &mut F,
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<F: fmt::Write, TContext: Into<Context>>(
38        &self,
39        f: &mut F,
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, 'b, 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    fn to_string<'a, 'b, TContext: Into<Context>>(&'a self, context: TContext) -> String {
74        self.display(context).to_string()
75    }
76}
77
78pub struct ContextDisplayable<'a, TValue, TContext>
79where
80    TValue: ContextualDisplay<TContext> + ?Sized,
81{
82    value: &'a TValue,
83    context: TContext,
84}
85
86impl<'a, 'b, TValue, TContext> fmt::Display for ContextDisplayable<'a, TValue, TContext>
87where
88    TValue: ContextualDisplay<TContext> + ?Sized,
89{
90    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
91        self.value
92            .contextual_format(f, &self.context)
93            .map_err(|_| fmt::Error) // We eat any errors into fmt::Error
94    }
95}