//! Definition of [`SmartDisplay`] and its related items. //! //! [`SmartDisplay`] is a trait that allows authors to provide additional information to both the //! formatter and other users. This information is provided in the form of a metadata type. The only //! required piece of metadata is the width of the value. This is _before_ it is passed to the //! formatter (i.e. it does not include any padding added by the formatter). Other information //! can be stored in a custom metadata type as needed. This information may be made available to //! downstream users, but it is not required. //! //! This module contains the [`SmartDisplay`] and associated items. //! //! # Example //! //! ```rust //! use std::fmt; //! //! use powerfmt::ext::FormatterExt as _; //! use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; //! //! #[derive(Debug)] //! struct User { //! id: usize, //! } //! //! // If you try to use `UserMetadata` in the `SmartDisplay` implementation, you will get a //! // compiler error about a private type being used publicly. To avoid this, use this attribute to //! // declare a private metadata type. You shouldn't need to worry about how this works, but be //! // aware that any public fields or methods remain usable by downstream users. //! #[smart_display::private_metadata] //! struct UserMetadata { //! username: String, //! legal_name: String, //! } //! //! // This attribute can be applied to `SmartDisplay` implementations. It will generate an //! // implementation of `Display` that delegates to `SmartDisplay`, avoiding the need to write //! // boilerplate. //! #[smart_display::delegate] //! impl SmartDisplay for User { //! type Metadata = UserMetadata; //! //! fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { //! // This could be obtained from a database, for example. //! let legal_name = "John Doe".to_owned(); //! let username = "jdoe".to_owned(); //! //! // Note that this must be kept in sync with the implementation of `fmt_with_metadata`. //! let width = smart_display::padded_width_of!(username, " (", legal_name, ")",); //! //! Metadata::new( //! width, //! self, //! UserMetadata { //! username, //! legal_name, //! }, //! ) //! } //! //! // Use the now-generated metadata to format the value. Here we use the `pad_with_width` //! // method to use the alignment and desired width from the formatter. //! fn fmt_with_metadata( //! &self, //! f: &mut fmt::Formatter<'_>, //! metadata: Metadata<Self>, //! ) -> fmt::Result { //! f.pad_with_width( //! metadata.unpadded_width(), //! format_args!("{} ({})", metadata.username, metadata.legal_name), //! ) //! } //! } //! //! let user = User { id: 42 }; //! assert_eq!(user.to_string(), "jdoe (John Doe)"); //! assert_eq!(format!("{user:>20}"), " jdoe (John Doe)"); //! ```
use core::cmp; use core::convert::Infallible; use core::fmt::{Alignment, Debug, Display, Formatter, Result}; use core::marker::PhantomData; use core::mem::MaybeUninit; use core::ops::Deref;
/// Compute the width of multiple items while optionally declaring the options for each item. /// /// ```rust /// # use powerfmt::smart_display; /// let alpha = 0; /// let beta = 1; /// let gamma = 100; /// /// let width = smart_display::padded_width_of!( /// alpha, // use the default options /// beta => width(2), // use the specified options /// gamma => width(2) sign_plus(true), // use multiple options /// ); /// assert_eq!(width, 7); /// /// let formatted = format!("{alpha}{beta:2}{gamma:+2}"); /// assert_eq!(formatted.len(), width); /// ``` /// /// Supported options are: /// /// Option | Method called /// --- | --- /// `fill(char)` | [`FormatterOptions::with_fill`] /// `sign_plus(bool)` | [`FormatterOptions::with_sign_plus`] /// `sign_minus(bool)` | [`FormatterOptions::with_sign_minus`] /// `align(Alignment)` | [`FormatterOptions::with_align`] /// `width(usize)` | [`FormatterOptions::with_width`] /// `precision(usize)` | [`FormatterOptions::with_precision`] /// `alternate(bool)` | [`FormatterOptions::with_alternate`] /// `sign_aware_zero_pad(bool)` | [`FormatterOptions::with_sign_aware_zero_pad`] /// /// If there are future additions to [`FormatterOptions`], they will be added to this macro as well. /// /// Options may be provided in any order and will be called in the order they are provided. The /// ordering matters if providing both `sign_plus` and `sign_minus`. #[cfg(doc)] #[doc(hidden)] // Don't show at crate root. #[macro_export]
macro_rules! padded_width_of {
($($t:tt)*) => {};
}
#[cfg(not(doc))] #[allow(missing_docs)] // This is done with `#[cfg(doc)]` to avoid showing the various rules. #[macro_export]
macro_rules! __not_public_at_root__padded_width_of { // Base case
(@inner [] [$($output:tt)+]) => { $($output)+ };
(@inner [$e:expr $(, $($remaining:tt)*)?] [$($expansion:tt)+]) => {
$crate::smart_display::padded_width_of!(@inner [$($($remaining)*)?] [
$($expansion)+ + $crate::smart_display::Metadata::padded_width_of(
&$e,
$crate::smart_display::padded_width_of!(@options)
)
])
};
(@inner
[$e:expr => $($call:ident($call_expr:expr))+ $(, $($remaining:tt)*)?]
[$($expansion:tt)+]
) => {
$crate::smart_display::padded_width_of!(@inner [$($($remaining)*)?] [
$($expansion)+ + $crate::smart_display::Metadata::padded_width_of(
&$e,
*$crate::smart_display::padded_width_of!(@options $($call($call_expr))+)
)
])
};
#[cfg(not(doc))] pubuse __not_public_at_root__padded_width_of as padded_width_of; #[cfg(doc)] #[doc(inline)] // Show in this module. pubuse padded_width_of; /// Implement [`Display`] for a type by using its implementation of [`SmartDisplay`]. /// /// This attribute is applied to the `SmartDisplay` implementation. /// /// ```rust,no_run /// # use powerfmt::smart_display::{self, SmartDisplay, Metadata, FormatterOptions}; /// # struct Foo; /// #[smart_display::delegate] /// impl SmartDisplay for Foo { /// # type Metadata = (); /// # fn metadata(&self, f: FormatterOptions) -> Metadata<Self> { /// # todo!() /// # } /// // ... /// } /// ``` #[cfg(feature = "macros")] pubuse powerfmt_macros::smart_display_delegate as delegate; /// Declare a private metadata type for `SmartDisplay`. /// /// Use this attribute if you want to provide metadata for a type that is not public. Doing /// this will avoid a compiler error about a private type being used publicly. Keep in mind /// that any public fields, public methods, and trait implementations _will_ be able to be used /// by downstream users. /// /// To avoid accidentally exposing details, such as when all fields are public or if the type /// is a unit struct, the type is annotated with `#[non_exhaustive]` automatically. /// /// ```rust,no_run /// # use powerfmt::smart_display; /// /// Metadata for `Foo` /// #[smart_display::private_metadata] /// #[derive(Debug)] /// pub(crate) struct FooMetadata { /// pub(crate) expensive_to_calculate: usize, /// } /// ``` #[cfg(feature = "macros")] pubuse powerfmt_macros::smart_display_private_metadata as private_metadata;
/// Configuration for formatting. /// /// This struct is obtained from a [`Formatter`]. It provides the same functionality as that of a /// reference to a `Formatter`. However, it is not possible to construct a `Formatter`, which is /// necessary for some use cases of [`SmartDisplay`]. `FormatterOptions` implements [`Default`] and /// has builder methods to alleviate this. #[derive(Clone, Copy)] pubstruct FormatterOptions {
flags: u8,
fill: char,
align: Option<Alignment>,
width: MaybeUninit<usize>,
precision: MaybeUninit<usize>,
}
impl FormatterOptions { /// Sets the fill character to use whenever there is alignment. #[inline] pubfn with_fill(&mutself, c: char) -> &mutSelf { self.fill = c; self
}
/// Set whether the `+` flag is specified. #[inline] pubfn with_sign_plus(&mutself, b: bool) -> &mutSelf { if b { self.flags |= 1 << FlagBit::SignPlus as u8; self.flags &= !(1 << FlagBit::SignMinus as u8);
} else { self.flags &= !(1 << FlagBit::SignPlus as u8);
} self
}
/// Set whether the `-` flag is specified. #[inline] pubfn with_sign_minus(&mutself, b: bool) -> &mutSelf { if b { self.flags |= 1 << FlagBit::SignMinus as u8; self.flags &= !(1 << FlagBit::SignPlus as u8);
} else { self.flags &= !(1 << FlagBit::SignMinus as u8);
} self
}
/// Set the flag indicating what form of alignment is requested, if any. #[inline] pubfn with_align(&mutself, align: Option<Alignment>) -> &='color:red'>mutSelf { self.align = align; self
}
/// Set the optional integer width that the output should be. #[inline] pubfn with_width(&mutself, width: Option<usize>) -> &mutSelf { iflet Some(width) = width { self.flags |= 1 << FlagBit::WidthIsInitialized as u8; self.width = MaybeUninit::new(width);
} else { self.flags &= !(1 << FlagBit::WidthIsInitialized as u8);
} self
}
/// Set the optional precision for numeric types. Alternatively, the maximum width for string /// types. #[inline] pubfn with_precision(&mutself, precision: Option<usize>) -> &tyle='color:red'>mutSelf { iflet Some(precision) = precision { self.flags |= 1 << FlagBit::PrecisionIsInitialized as u8; self.precision = MaybeUninit::new(precision);
} else { self.flags &= !(1 << FlagBit::PrecisionIsInitialized as u8);
} self
}
/// Set whether the `#` flag is specified. #[inline] pubfn with_alternate(&mutself, b: bool) -> &mutSelf { if b { self.flags |= 1 << FlagBit::Alternate as u8;
} else { self.flags &= !(1 << FlagBit::Alternate as u8);
} self
}
/// Set whether the `0` flag is specified. #[inline] pubfn with_sign_aware_zero_pad(&mutself, b: bool) -> &'color:red'>mutSelf { if b { self.flags |= 1 << FlagBit::SignAwareZeroPad as u8;
} else { self.flags &= !(1 << FlagBit::SignAwareZeroPad as u8);
} self
}
}
impl FormatterOptions { /// Character used as 'fill' whenever there is alignment. #[inline] #[must_use] pubconstfn fill(&self) -> char { self.fill
}
/// Flag indicating what form of alignment was requested. #[inline] #[must_use] pubconstfn align(&self) -> Option<Alignment> { self.align
}
/// Optionally specified integer width that the output should be. #[inline] #[must_use] pubconstfn width(&self) -> Option<usize> { if (self.flags >> FlagBit::WidthIsInitialized as u8) & 1 == 1 { // Safety: `width` is initialized if the flag is set.
Some(unsafe { self.width.assume_init() })
} else {
None
}
}
/// Optionally specified precision for numeric types. Alternatively, the maximum width for /// string types. #[inline] #[must_use] pubconstfn precision(&self) -> Option<usize> { if (self.flags >> FlagBit::PrecisionIsInitialized as u8) & 1 == 1 { // Safety: `precision` is initialized if the flag is set.
Some(unsafe { self.precision.assume_init() })
} else {
None
}
}
/// Determines if the `+` flag was specified. #[inline] #[must_use] pubconstfn sign_plus(&self) -> bool {
(self.flags >> FlagBit::SignPlus as u8) & 1 == 1
}
/// Determines if the `-` flag was specified. #[inline] #[must_use] pubconstfn sign_minus(&self) -> bool {
(self.flags >> FlagBit::SignMinus as u8) & 1 == 1
}
/// Determines if the `#` flag was specified. #[inline] #[must_use] pubconstfn alternate(&self) -> bool {
(self.flags >> FlagBit::Alternate as u8) & 1 == 1
}
/// Determines if the `0` flag was specified. #[inline] #[must_use] pubconstfn sign_aware_zero_pad(&self) -> bool {
(self.flags >> FlagBit::SignAwareZeroPad as u8) & 1 == 1
}
}
/// Information used to format a value. This is returned by [`SmartDisplay::metadata`]. /// /// This type is generic over any user-provided type. This allows the author to store any /// information that is needed. For example, a type's implementation of [`SmartDisplay`] may need /// to calculate something before knowing its width. This calculation can be performed, with the /// result being stored in the custom metadata type. /// /// Note that `Metadata` _always_ contains the width of the type. Authors do not need to store this /// information in their custom metadata type. /// /// Generally speaking, a type should be able to be formatted using only its metadata, fields, and /// the formatter. Any other information should be stored in the metadata type. pubstruct Metadata<'a, T> where
T: SmartDisplay + ?Sized,
{
unpadded_width: usize,
metadata: T::Metadata,
_value: PhantomData<&'a T>, // variance
}
// manual impls for bounds impl<T> Debug for Metadata<'_, T> where
T: SmartDisplay,
T::Metadata: Debug,
{ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("Metadata")
.field("unpadded_width", &self.unpadded_width)
.field("metadata", &self.metadata)
.finish()
}
}
impl<T> Copy for Metadata<'_, T> where
T: SmartDisplay,
T::Metadata: Copy,
{
}
impl<'a, T> Metadata<'a, T> where
T: SmartDisplay + ?Sized,
{ /// Creates a new `Metadata` with the given width and metadata. While the width _should_ be /// exact, this is not a requirement for soundness. pubconstfn new(unpadded_width: usize, _value: &T, metadata: T::Metadata) -> Self { Self {
unpadded_width,
metadata,
_value: PhantomData,
}
}
/// Reuse the metadata for another type. This is useful when implementing [`SmartDisplay`] for a /// type that wraps another type. Both type's metadata type must be the same. pubfn reuse<'b, U>(self) -> Metadata<'b, U> where 'a: 'b,
U: SmartDisplay<Metadata = T::Metadata> + ?Sized,
{
Metadata {
unpadded_width: self.unpadded_width,
metadata: self.metadata,
_value: PhantomData,
}
}
/// Obtain the width of the value before padding. pubconstfn unpadded_width(&self) -> usize { self.unpadded_width
}
/// Obtain the width of the value after padding. pubfn padded_width(&self, f: FormatterOptions) -> usize { match f.width() {
Some(requested_width) => cmp::max(self.unpadded_width(), requested_width),
None => self.unpadded_width(),
}
}
}
impl Metadata<'_, Infallible> { /// Obtain the width of the value before padding, given the formatter options. pubfn unpadded_width_of<T>(value: T, f: FormatterOptions) -> usize where
T: SmartDisplay,
{
value.metadata(f).unpadded_width
}
/// Obtain the width of the value after padding, given the formatter options. pubfn padded_width_of<T>(value: T, f: FormatterOptions) -> usize where
T: SmartDisplay,
{
value.metadata(f).padded_width(f)
}
}
/// Permit using `Metadata` as a smart pointer to the user-provided metadata. impl<T> Deref for Metadata<'_, T> where
T: SmartDisplay + ?Sized,
{ type Target = T::Metadata;
/// Format trait that allows authors to provide additional information. /// /// This trait is similar to [`Display`], but allows the author to provide additional information /// to the formatter. This information is provided in the form of a custom metadata type. /// /// The only required piece of metadata is the width of the value. This is _before_ it is passed to /// the formatter (i.e. it does not include any padding added by the formatter). Other information /// can be stored in a custom metadata type as needed. This information may be made available to /// downstream users, but it is not required. /// /// **Note**: While both `fmt_with_metadata` and `fmt` have default implementations, it is strongly /// recommended to implement only `fmt_with_metadata`. `fmt` should be implemented if and only if /// the type does not require any of the calculated metadata. In that situation, `fmt_with_metadata` /// should be omitted. #[cfg_attr(__powerfmt_docs, rustc_must_implement_one_of(fmt, fmt_with_metadata))] pubtrait SmartDisplay: Display { /// User-provided metadata type. type Metadata;
/// Compute any information needed to format the value. This must, at a minimum, determine the /// width of the value before any padding is added by the formatter. /// /// If the type uses other types that implement `SmartDisplay` verbatim, the inner types should /// have their metadata calculated and included in the returned value. /// /// # Lifetimes /// /// This method's return type contains a lifetime to `self`. This ensures that the metadata will /// neither outlive the value nor be invalidated by a mutation of the value (barring interior /// mutability). /// /// ```rust,compile_fail /// # use std::fmt; /// # use std::fmt::Write; /// # use powerfmt::buf::WriteBuffer; /// # use powerfmt::smart_display::{self, FormatterOptions, Metadata, SmartDisplay}; /// #[derive(Debug)] /// struct WrappedBuffer(WriteBuffer<128>); /// /// #[smart_display::delegate] /// impl SmartDisplay for WrappedBuffer { /// type Metadata = (); /// /// fn metadata(&self, _: FormatterOptions) -> Metadata<'_, Self> { /// Metadata::new(self.0.len(), self, ()) /// } /// /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// f.pad(self.0.as_str()) /// } /// } /// /// let mut buf = WrappedBuffer(WriteBuffer::new()); /// let metadata = buf.metadata(FormatterOptions::default()); /// // We cannot mutate the buffer while it is borrowed and use its previous metadata on the /// // following line. /// write!(buf.0, "Hello, world!")?; /// assert_eq!(metadata.width(), 13); /// # Ok::<(), Box<dyn std::error::Error>>(()) /// ``` fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self>;
/// Format the value using the given formatter and metadata. The formatted output should have /// the width indicated by the metadata. This is before any padding is added by the /// formatter. /// /// If the metadata is not needed, you should implement the `fmt` method instead. fn fmt_with_metadata(&self, f: &mut Formatter<'_>, _metadata: Metadata<'_, Self>) -> Result {
SmartDisplay::fmt(self, f)
}
/// Format the value using the given formatter. This is the same as [`Display::fmt`]. /// /// The default implementation of this method calls `fmt_with_metadata` with the result of /// `metadata`. Generally speaking, this method should not be implemented. You should implement /// the `fmt_with_metadata` method instead. fn fmt(&self, f: &mut Formatter<'_>) -> Result { let metadata = self.metadata(f.into()); self.fmt_with_metadata(f, metadata)
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.