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;
#[cfg(doc)]
#[doc(hidden)] #[macro_export]
macro_rules! padded_width_of {
($($t:tt)*) => {};
}
#[cfg(not(doc))]
#[allow(missing_docs)] #[macro_export]
macro_rules! __not_public_at_root__padded_width_of {
(@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))+)
)
])
};
(@options_inner [] [$($output:tt)+]) => { $($output)+ };
(@options_inner [fill($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_fill($e)
])
};
(@options_inner [sign_plus($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_sign_plus($e)
])
};
(@options_inner [sign_minus($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_sign_minus($e)
])
};
(@options_inner [align($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_align(Some($e))
])
};
(@options_inner [width($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_width(Some($e))
])
};
(@options_inner [precision($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_precision(Some($e))
])
};
(@options_inner [alternate($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_width($e)
])
};
(@options_inner [sign_aware_zero_pad($e:expr) $($remaining:tt)*] [$($expansion:tt)*]) => {
$crate::smart_display::padded_width_of!(@options_inner [$($remaining)*] [
$($expansion)*.with_sign_aware_zero_pad($e)
])
};
(@options $($e:tt)*) => {
$crate::smart_display::padded_width_of!(@options_inner [$($e)*] [
$crate::smart_display::FormatterOptions::default()
])
};
($($t:tt)*) => {
$crate::smart_display::padded_width_of!(
@inner [$($t)*] [0]
)
};
}
#[cfg(not(doc))]
pub use __not_public_at_root__padded_width_of as padded_width_of;
#[cfg(doc)]
#[doc(inline)] pub use padded_width_of;
#[cfg(feature = "macros")]
pub use powerfmt_macros::smart_display_delegate as delegate;
#[cfg(feature = "macros")]
pub use powerfmt_macros::smart_display_private_metadata as private_metadata;
#[derive(Debug)]
enum FlagBit {
SignPlus,
SignMinus,
Alternate,
SignAwareZeroPad,
WidthIsInitialized,
PrecisionIsInitialized,
}
#[derive(Clone, Copy)]
pub struct FormatterOptions {
flags: u8,
fill: char,
align: Option<Alignment>,
width: MaybeUninit<usize>,
precision: MaybeUninit<usize>,
}
impl Debug for FormatterOptions {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
f.debug_struct("FormatterOptions")
.field("fill", &self.fill)
.field("align", &self.align())
.field("width", &self.width())
.field("precision", &self.precision())
.field("sign_plus", &self.sign_plus())
.field("sign_minus", &self.sign_minus())
.field("alternate", &self.alternate())
.field("sign_aware_zero_pad", &self.sign_aware_zero_pad())
.finish()
}
}
impl Default for FormatterOptions {
#[inline]
fn default() -> Self {
Self {
flags: 0,
fill: ' ',
align: None,
width: MaybeUninit::uninit(),
precision: MaybeUninit::uninit(),
}
}
}
impl FormatterOptions {
#[inline]
pub fn with_fill(&mut self, c: char) -> &mut Self {
self.fill = c;
self
}
#[inline]
pub fn with_sign_plus(&mut self, b: bool) -> &mut Self {
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
}
#[inline]
pub fn with_sign_minus(&mut self, b: bool) -> &mut Self {
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
}
#[inline]
pub fn with_align(&mut self, align: Option<Alignment>) -> &mut Self {
self.align = align;
self
}
#[inline]
pub fn with_width(&mut self, width: Option<usize>) -> &mut Self {
if let Some(width) = width {
self.flags |= 1 << FlagBit::WidthIsInitialized as u8;
self.width = MaybeUninit::new(width);
} else {
self.flags &= !(1 << FlagBit::WidthIsInitialized as u8);
}
self
}
#[inline]
pub fn with_precision(&mut self, precision: Option<usize>) -> &mut Self {
if let Some(precision) = precision {
self.flags |= 1 << FlagBit::PrecisionIsInitialized as u8;
self.precision = MaybeUninit::new(precision);
} else {
self.flags &= !(1 << FlagBit::PrecisionIsInitialized as u8);
}
self
}
#[inline]
pub fn with_alternate(&mut self, b: bool) -> &mut Self {
if b {
self.flags |= 1 << FlagBit::Alternate as u8;
} else {
self.flags &= !(1 << FlagBit::Alternate as u8);
}
self
}
#[inline]
pub fn with_sign_aware_zero_pad(&mut self, b: bool) -> &mut Self {
if b {
self.flags |= 1 << FlagBit::SignAwareZeroPad as u8;
} else {
self.flags &= !(1 << FlagBit::SignAwareZeroPad as u8);
}
self
}
}
impl FormatterOptions {
#[inline]
#[must_use]
pub const fn fill(&self) -> char {
self.fill
}
#[inline]
#[must_use]
pub const fn align(&self) -> Option<Alignment> {
self.align
}
#[inline]
#[must_use]
pub const fn width(&self) -> Option<usize> {
if (self.flags >> FlagBit::WidthIsInitialized as u8) & 1 == 1 {
Some(unsafe { self.width.assume_init() })
} else {
None
}
}
#[inline]
#[must_use]
pub const fn precision(&self) -> Option<usize> {
if (self.flags >> FlagBit::PrecisionIsInitialized as u8) & 1 == 1 {
Some(unsafe { self.precision.assume_init() })
} else {
None
}
}
#[inline]
#[must_use]
pub const fn sign_plus(&self) -> bool {
(self.flags >> FlagBit::SignPlus as u8) & 1 == 1
}
#[inline]
#[must_use]
pub const fn sign_minus(&self) -> bool {
(self.flags >> FlagBit::SignMinus as u8) & 1 == 1
}
#[inline]
#[must_use]
pub const fn alternate(&self) -> bool {
(self.flags >> FlagBit::Alternate as u8) & 1 == 1
}
#[inline]
#[must_use]
pub const fn sign_aware_zero_pad(&self) -> bool {
(self.flags >> FlagBit::SignAwareZeroPad as u8) & 1 == 1
}
}
impl From<&Formatter<'_>> for FormatterOptions {
fn from(value: &Formatter<'_>) -> Self {
*Self::default()
.with_fill(value.fill())
.with_sign_plus(value.sign_plus())
.with_sign_minus(value.sign_minus())
.with_align(value.align())
.with_width(value.width())
.with_precision(value.precision())
.with_alternate(value.alternate())
.with_sign_aware_zero_pad(value.sign_aware_zero_pad())
}
}
impl From<&mut Formatter<'_>> for FormatterOptions {
#[inline]
fn from(value: &mut Formatter<'_>) -> Self {
(&*value).into()
}
}
pub struct Metadata<'a, T>
where
T: SmartDisplay + ?Sized,
{
unpadded_width: usize,
metadata: T::Metadata,
_value: PhantomData<&'a T>, }
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> Clone for Metadata<'_, T>
where
T: SmartDisplay,
T::Metadata: Clone,
{
fn clone(&self) -> Self {
Self {
unpadded_width: self.unpadded_width,
metadata: self.metadata.clone(),
_value: self._value,
}
}
}
impl<T> Copy for Metadata<'_, T>
where
T: SmartDisplay,
T::Metadata: Copy,
{
}
impl<'a, T> Metadata<'a, T>
where
T: SmartDisplay + ?Sized,
{
pub const fn new(unpadded_width: usize, _value: &T, metadata: T::Metadata) -> Self {
Self {
unpadded_width,
metadata,
_value: PhantomData,
}
}
pub fn 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,
}
}
pub const fn unpadded_width(&self) -> usize {
self.unpadded_width
}
pub fn 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> {
pub fn unpadded_width_of<T>(value: T, f: FormatterOptions) -> usize
where
T: SmartDisplay,
{
value.metadata(f).unpadded_width
}
pub fn padded_width_of<T>(value: T, f: FormatterOptions) -> usize
where
T: SmartDisplay,
{
value.metadata(f).padded_width(f)
}
}
impl<T> Deref for Metadata<'_, T>
where
T: SmartDisplay + ?Sized,
{
type Target = T::Metadata;
fn deref(&self) -> &T::Metadata {
&self.metadata
}
}
#[cfg_attr(__powerfmt_docs, rustc_must_implement_one_of(fmt, fmt_with_metadata))]
pub trait SmartDisplay: Display {
type Metadata;
fn metadata(&self, f: FormatterOptions) -> Metadata<'_, Self>;
fn fmt_with_metadata(&self, f: &mut Formatter<'_>, _metadata: Metadata<'_, Self>) -> Result {
SmartDisplay::fmt(self, f)
}
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let metadata = self.metadata(f.into());
self.fmt_with_metadata(f, metadata)
}
}