Added struct fmt::FormattingOptions
This allows to build custom `std::Formatter`s at runtime. Also added some related enums and two related methods on `std::Formatter`.
This commit is contained in:
parent
1d7984a132
commit
f17d13285c
8 changed files with 349 additions and 70 deletions
|
@ -596,6 +596,8 @@ pub use core::fmt::{Arguments, write};
|
||||||
pub use core::fmt::{Binary, Octal};
|
pub use core::fmt::{Binary, Octal};
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub use core::fmt::{Debug, Display};
|
pub use core::fmt::{Debug, Display};
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub use core::fmt::{DebugAsHex, FormattingOptions, Sign};
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
|
pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
|
@ -117,6 +117,7 @@
|
||||||
#![feature(extend_one_unchecked)]
|
#![feature(extend_one_unchecked)]
|
||||||
#![feature(fmt_internals)]
|
#![feature(fmt_internals)]
|
||||||
#![feature(fn_traits)]
|
#![feature(fn_traits)]
|
||||||
|
#![feature(formatting_options)]
|
||||||
#![feature(hasher_prefixfree_extras)]
|
#![feature(hasher_prefixfree_extras)]
|
||||||
#![feature(inplace_iteration)]
|
#![feature(inplace_iteration)]
|
||||||
#![feature(iter_advance_by)]
|
#![feature(iter_advance_by)]
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
#![stable(feature = "rust1", since = "1.0.0")]
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
||||||
use core::error::Error;
|
use core::error::Error;
|
||||||
|
use core::fmt::FormattingOptions;
|
||||||
use core::iter::FusedIterator;
|
use core::iter::FusedIterator;
|
||||||
#[cfg(not(no_global_oom_handling))]
|
#[cfg(not(no_global_oom_handling))]
|
||||||
use core::iter::from_fn;
|
use core::iter::from_fn;
|
||||||
|
@ -2682,7 +2683,7 @@ impl<T: fmt::Display + ?Sized> ToString for T {
|
||||||
#[inline]
|
#[inline]
|
||||||
default fn to_string(&self) -> String {
|
default fn to_string(&self) -> String {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let mut formatter = core::fmt::Formatter::new(&mut buf);
|
let mut formatter = core::fmt::Formatter::new(&mut buf, FormattingOptions::new());
|
||||||
// Bypass format_args!() to avoid write_str with zero-length strs
|
// Bypass format_args!() to avoid write_str with zero-length strs
|
||||||
fmt::Display::fmt(self, &mut formatter)
|
fmt::Display::fmt(self, &mut formatter)
|
||||||
.expect("a Display implementation returned an error unexpectedly");
|
.expect("a Display implementation returned an error unexpectedly");
|
||||||
|
|
|
@ -33,6 +33,19 @@ pub enum Alignment {
|
||||||
Center,
|
Center,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")]
|
||||||
|
impl From<rt::Alignment> for Option<Alignment> {
|
||||||
|
fn from(value: rt::Alignment) -> Self {
|
||||||
|
match value {
|
||||||
|
rt::Alignment::Left => Some(Alignment::Left),
|
||||||
|
rt::Alignment::Right => Some(Alignment::Right),
|
||||||
|
rt::Alignment::Center => Some(Alignment::Center),
|
||||||
|
rt::Alignment::Unknown => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[stable(feature = "debug_builders", since = "1.2.0")]
|
#[stable(feature = "debug_builders", since = "1.2.0")]
|
||||||
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
|
pub use self::builders::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
|
||||||
#[unstable(feature = "debug_closure_helpers", issue = "117729")]
|
#[unstable(feature = "debug_closure_helpers", issue = "117729")]
|
||||||
|
@ -247,6 +260,243 @@ impl<W: Write + ?Sized> Write for &mut W {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The signedness of a [`Formatter`] (or of a [`FormattingOptions`]).
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub enum Sign {
|
||||||
|
/// Represents the `+` flag.
|
||||||
|
Plus,
|
||||||
|
/// Represents the `-` flag.
|
||||||
|
Minus,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies whether the [`Debug`] trait should use lower-/upper-case
|
||||||
|
/// hexadecimal or normal integers.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub enum DebugAsHex {
|
||||||
|
/// Use lower-case hexadecimal integers for the `Debug` trait (like [the `x?` type](../../std/fmt/index.html#formatting-traits)).
|
||||||
|
Lower,
|
||||||
|
/// Use upper-case hexadecimal integers for the `Debug` trait (like [the `x?` type](../../std/fmt/index.html#formatting-traits)).
|
||||||
|
Upper,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Options for formatting.
|
||||||
|
///
|
||||||
|
/// `FormattingOptions` is a [`Formatter`] without an attached [`Write`] trait.
|
||||||
|
/// It is mainly used to construct `Formatter` instances.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub struct FormattingOptions {
|
||||||
|
sign: Option<Sign>,
|
||||||
|
sign_aware_zero_pad: bool,
|
||||||
|
alternate: bool,
|
||||||
|
fill: char,
|
||||||
|
alignment: Option<Alignment>,
|
||||||
|
width: Option<usize>,
|
||||||
|
precision: Option<usize>,
|
||||||
|
debug_as_hex: Option<DebugAsHex>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FormattingOptions {
|
||||||
|
/// Construct a new `FormatterBuilder` with the supplied `Write` trait
|
||||||
|
/// object for output that is equivalent to the `{}` formatting
|
||||||
|
/// specifier:
|
||||||
|
///
|
||||||
|
/// - no flags,
|
||||||
|
/// - filled with spaces,
|
||||||
|
/// - no alignment,
|
||||||
|
/// - no width,
|
||||||
|
/// - no precision, and
|
||||||
|
/// - no [`DebugAsHex`] output mode.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
sign: None,
|
||||||
|
sign_aware_zero_pad: false,
|
||||||
|
alternate: false,
|
||||||
|
fill: ' ',
|
||||||
|
alignment: None,
|
||||||
|
width: None,
|
||||||
|
precision: None,
|
||||||
|
debug_as_hex: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets or removes the sign (the `+` or the `-` flag).
|
||||||
|
///
|
||||||
|
/// - `+`: This is intended for numeric types and indicates that the sign
|
||||||
|
/// should always be printed. By default only the negative sign of signed
|
||||||
|
/// values is printed, and the sign of positive or unsigned values is
|
||||||
|
/// omitted. This flag indicates that the correct sign (+ or -) should
|
||||||
|
/// always be printed.
|
||||||
|
/// - `-`: Currently not used
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn sign(&mut self, sign: Option<Sign>) -> &mut Self {
|
||||||
|
self.sign = sign;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets or unsets the `0` flag.
|
||||||
|
///
|
||||||
|
/// This is used to indicate for integer formats that the padding to width should both be done with a 0 character as well as be sign-aware
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn sign_aware_zero_pad(&mut self, sign_aware_zero_pad: bool) -> &mut Self {
|
||||||
|
self.sign_aware_zero_pad = sign_aware_zero_pad;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets or unsets the `#` flag.
|
||||||
|
///
|
||||||
|
/// This flag indicates that the "alternate" form of printing should be
|
||||||
|
/// used. The alternate forms are:
|
||||||
|
/// - [`Debug`] : pretty-print the [`Debug`] formatting (adds linebreaks and indentation)
|
||||||
|
/// - [`LowerHex`] as well as [`UpperHex`] - precedes the argument with a `0x`
|
||||||
|
/// - [`Octal`] - precedes the argument with a `0b`
|
||||||
|
/// - [`Binary`] - precedes the argument with a `0o`
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn alternate(&mut self, alternate: bool) -> &mut Self {
|
||||||
|
self.alternate = alternate;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets the fill character.
|
||||||
|
///
|
||||||
|
/// The optional fill character and alignment is provided normally in
|
||||||
|
/// conjunction with the width parameter. This indicates that if the value
|
||||||
|
/// being formatted is smaller than width some extra characters will be
|
||||||
|
/// printed around it.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn fill(&mut self, fill: char) -> &mut Self {
|
||||||
|
self.fill = fill;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets or removes the alignment.
|
||||||
|
///
|
||||||
|
/// The alignment specifies how the value being formatted should be
|
||||||
|
/// positioned if it is smaller than the width of the formatter.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn alignment(&mut self, alignment: Option<Alignment>) -> &mut Self {
|
||||||
|
self.alignment = alignment;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets or removes the width.
|
||||||
|
///
|
||||||
|
/// This is a parameter for the “minimum width” that the format should take
|
||||||
|
/// up. If the value’s string does not fill up this many characters, then
|
||||||
|
/// the padding specified by [`FormattingOptions::fill`]/[`FormattingOptions::alignment`]
|
||||||
|
/// will be used to take up the required space.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn width(&mut self, width: Option<usize>) -> &mut Self {
|
||||||
|
self.width = width;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Sets or removes the precision.
|
||||||
|
///
|
||||||
|
/// - For non-numeric types, this can be considered a “maximum width”. If
|
||||||
|
/// the resulting string is longer than this width, then it is truncated
|
||||||
|
/// down to this many characters and that truncated value is emitted with
|
||||||
|
/// proper fill, alignment and width if those parameters are set.
|
||||||
|
/// - For integral types, this is ignored.
|
||||||
|
/// - For floating-point types, this indicates how many digits after the
|
||||||
|
/// decimal point should be printed.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn precision(&mut self, precision: Option<usize>) -> &mut Self {
|
||||||
|
self.precision = precision;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
/// Specifies whether the [`Debug`] trait should use lower-/upper-case
|
||||||
|
/// hexadecimal or normal integers
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn debug_as_hex(&mut self, debug_as_hex: Option<DebugAsHex>) -> &mut Self {
|
||||||
|
self.debug_as_hex = debug_as_hex;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current sign (the `+` or the `-` flag).
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_sign(&self) -> Option<Sign> {
|
||||||
|
self.sign
|
||||||
|
}
|
||||||
|
/// Returns the current `0` flag.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_sign_aware_zero_pad(&self) -> bool {
|
||||||
|
self.sign_aware_zero_pad
|
||||||
|
}
|
||||||
|
/// Returns the current `#` flag.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_alternate(&self) -> bool {
|
||||||
|
self.alternate
|
||||||
|
}
|
||||||
|
/// Returns the current fill character.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_fill(&self) -> char {
|
||||||
|
self.fill
|
||||||
|
}
|
||||||
|
/// Returns the current alignment.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_alignment(&self) -> Option<Alignment> {
|
||||||
|
self.alignment
|
||||||
|
}
|
||||||
|
/// Returns the current width.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_width(&self) -> Option<usize> {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
/// Returns the current precision.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_precision(&self) -> Option<usize> {
|
||||||
|
self.precision
|
||||||
|
}
|
||||||
|
/// Returns the current precision.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn get_debug_as_hex(&self) -> Option<DebugAsHex> {
|
||||||
|
self.debug_as_hex
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`Formatter`] that writes its output to the given [`Write`] trait.
|
||||||
|
///
|
||||||
|
/// You may alternatively use [`Formatter::new()`].
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> {
|
||||||
|
Formatter { options: self, buf: write }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")]
|
||||||
|
/// Flags for formatting
|
||||||
|
pub fn flags(&mut self, flags: u32) {
|
||||||
|
self.sign = if flags & (1 << rt::Flag::SignPlus as u32) != 0 {
|
||||||
|
Some(Sign::Plus)
|
||||||
|
} else if flags & (1 << rt::Flag::SignMinus as u32) != 0 {
|
||||||
|
Some(Sign::Minus)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.alternate = (flags & (1 << rt::Flag::Alternate as u32)) != 0;
|
||||||
|
self.sign_aware_zero_pad = (flags & (1 << rt::Flag::SignAwareZeroPad as u32)) != 0;
|
||||||
|
self.debug_as_hex = if flags & (1 << rt::Flag::DebugLowerHex as u32) != 0 {
|
||||||
|
Some(DebugAsHex::Lower)
|
||||||
|
} else if flags & (1 << rt::Flag::DebugUpperHex as u32) != 0 {
|
||||||
|
Some(DebugAsHex::Upper)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#[doc(hidden)]
|
||||||
|
#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")]
|
||||||
|
/// Flags for formatting
|
||||||
|
pub fn get_flags(&self) -> u32 {
|
||||||
|
<bool as Into<u32>>::into(self.get_sign() == Some(Sign::Plus)) << rt::Flag::SignPlus as u32
|
||||||
|
| <bool as Into<u32>>::into(self.get_sign() == Some(Sign::Minus))
|
||||||
|
<< rt::Flag::SignMinus as u32
|
||||||
|
| <bool as Into<u32>>::into(self.get_alternate()) << rt::Flag::Alternate as u32
|
||||||
|
| <bool as Into<u32>>::into(self.get_sign_aware_zero_pad())
|
||||||
|
<< rt::Flag::SignAwareZeroPad as u32
|
||||||
|
| <bool as Into<u32>>::into(self.debug_as_hex == Some(DebugAsHex::Lower))
|
||||||
|
<< rt::Flag::DebugLowerHex as u32
|
||||||
|
| <bool as Into<u32>>::into(self.debug_as_hex == Some(DebugAsHex::Upper))
|
||||||
|
<< rt::Flag::DebugUpperHex as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Configuration for formatting.
|
/// Configuration for formatting.
|
||||||
///
|
///
|
||||||
/// A `Formatter` represents various options related to formatting. Users do not
|
/// A `Formatter` represents various options related to formatting. Users do not
|
||||||
|
@ -260,34 +510,28 @@ impl<W: Write + ?Sized> Write for &mut W {
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[rustc_diagnostic_item = "Formatter"]
|
#[rustc_diagnostic_item = "Formatter"]
|
||||||
pub struct Formatter<'a> {
|
pub struct Formatter<'a> {
|
||||||
flags: u32,
|
options: FormattingOptions,
|
||||||
fill: char,
|
|
||||||
align: rt::Alignment,
|
|
||||||
width: Option<usize>,
|
|
||||||
precision: Option<usize>,
|
|
||||||
|
|
||||||
buf: &'a mut (dyn Write + 'a),
|
buf: &'a mut (dyn Write + 'a),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Formatter<'a> {
|
impl<'a> Formatter<'a> {
|
||||||
/// Creates a new formatter with default settings.
|
/// Creates a new formatter with given [`FormattingOptions`].
|
||||||
///
|
///
|
||||||
/// This can be used as a micro-optimization in cases where a full `Arguments`
|
/// If `write` is a reference to a formatter, it is recommended to use
|
||||||
/// structure (as created by `format_args!`) is not necessary; `Arguments`
|
/// [`Formatter::with_options`] instead as this can borrow the underlying
|
||||||
/// is a little more expensive to use in simple formatting scenarios.
|
/// `write`, thereby bypassing one layer of indirection.
|
||||||
///
|
///
|
||||||
/// Currently not intended for use outside of the standard library.
|
/// You may alternatively use [`FormattingOptions::create_formatter()`].
|
||||||
#[unstable(feature = "fmt_internals", reason = "internal to standard library", issue = "none")]
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
#[doc(hidden)]
|
pub fn new(write: &'a mut (dyn Write + 'a), options: FormattingOptions) -> Self {
|
||||||
pub fn new(buf: &'a mut (dyn Write + 'a)) -> Formatter<'a> {
|
Formatter { options, buf: write }
|
||||||
Formatter {
|
}
|
||||||
flags: 0,
|
|
||||||
fill: ' ',
|
/// Creates a new formatter based on this one with given [`FormattingOptions`].
|
||||||
align: rt::Alignment::Unknown,
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
width: None,
|
pub fn with_options(&'a mut self, options: FormattingOptions) -> Self {
|
||||||
precision: None,
|
Formatter { options, buf: self.buf }
|
||||||
buf,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,7 +1409,7 @@ pub trait UpperExp {
|
||||||
/// [`write!`]: crate::write!
|
/// [`write!`]: crate::write!
|
||||||
#[stable(feature = "rust1", since = "1.0.0")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
|
pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
|
||||||
let mut formatter = Formatter::new(output);
|
let mut formatter = Formatter::new(output, FormattingOptions::new());
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
|
|
||||||
match args.fmt {
|
match args.fmt {
|
||||||
|
@ -1214,14 +1458,14 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result {
|
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result {
|
||||||
fmt.fill = arg.fill;
|
fmt.options.fill(arg.fill);
|
||||||
fmt.align = arg.align;
|
fmt.options.alignment(arg.align.into());
|
||||||
fmt.flags = arg.flags;
|
fmt.options.flags(arg.flags);
|
||||||
// SAFETY: arg and args come from the same Arguments,
|
// SAFETY: arg and args come from the same Arguments,
|
||||||
// which guarantees the indexes are always within bounds.
|
// which guarantees the indexes are always within bounds.
|
||||||
unsafe {
|
unsafe {
|
||||||
fmt.width = getcount(args, &arg.width);
|
fmt.options.width(getcount(args, &arg.width));
|
||||||
fmt.precision = getcount(args, &arg.precision);
|
fmt.options.precision(getcount(args, &arg.precision));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract the correct argument
|
// Extract the correct argument
|
||||||
|
@ -1280,11 +1524,7 @@ impl<'a> Formatter<'a> {
|
||||||
buf: wrap(self.buf),
|
buf: wrap(self.buf),
|
||||||
|
|
||||||
// And preserve these
|
// And preserve these
|
||||||
flags: self.flags,
|
options: self.options,
|
||||||
fill: self.fill,
|
|
||||||
align: self.align,
|
|
||||||
width: self.width,
|
|
||||||
precision: self.precision,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1381,14 +1621,15 @@ impl<'a> Formatter<'a> {
|
||||||
// The sign and prefix goes before the padding if the fill character
|
// The sign and prefix goes before the padding if the fill character
|
||||||
// is zero
|
// is zero
|
||||||
Some(min) if self.sign_aware_zero_pad() => {
|
Some(min) if self.sign_aware_zero_pad() => {
|
||||||
let old_fill = crate::mem::replace(&mut self.fill, '0');
|
let old_fill = crate::mem::replace(&mut self.options.fill, '0');
|
||||||
let old_align = crate::mem::replace(&mut self.align, rt::Alignment::Right);
|
let old_align =
|
||||||
|
crate::mem::replace(&mut self.options.alignment, Some(Alignment::Right));
|
||||||
write_prefix(self, sign, prefix)?;
|
write_prefix(self, sign, prefix)?;
|
||||||
let post_padding = self.padding(min - width, Alignment::Right)?;
|
let post_padding = self.padding(min - width, Alignment::Right)?;
|
||||||
self.buf.write_str(buf)?;
|
self.buf.write_str(buf)?;
|
||||||
post_padding.write(self)?;
|
post_padding.write(self)?;
|
||||||
self.fill = old_fill;
|
self.options.fill = old_fill;
|
||||||
self.align = old_align;
|
self.options.alignment = old_align;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// Otherwise, the sign and prefix goes after the padding
|
// Otherwise, the sign and prefix goes after the padding
|
||||||
|
@ -1487,12 +1728,7 @@ impl<'a> Formatter<'a> {
|
||||||
padding: usize,
|
padding: usize,
|
||||||
default: Alignment,
|
default: Alignment,
|
||||||
) -> result::Result<PostPadding, Error> {
|
) -> result::Result<PostPadding, Error> {
|
||||||
let align = match self.align {
|
let align = self.align().unwrap_or(default);
|
||||||
rt::Alignment::Unknown => default,
|
|
||||||
rt::Alignment::Left => Alignment::Left,
|
|
||||||
rt::Alignment::Right => Alignment::Right,
|
|
||||||
rt::Alignment::Center => Alignment::Center,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (pre_pad, post_pad) = match align {
|
let (pre_pad, post_pad) = match align {
|
||||||
Alignment::Left => (0, padding),
|
Alignment::Left => (0, padding),
|
||||||
|
@ -1530,8 +1766,8 @@ impl<'a> Formatter<'a> {
|
||||||
// remove the sign from the formatted parts
|
// remove the sign from the formatted parts
|
||||||
formatted.sign = "";
|
formatted.sign = "";
|
||||||
width = width.saturating_sub(sign.len());
|
width = width.saturating_sub(sign.len());
|
||||||
self.fill = '0';
|
self.options.fill('0');
|
||||||
self.align = rt::Alignment::Right;
|
self.options.alignment(Some(Alignment::Right));
|
||||||
}
|
}
|
||||||
|
|
||||||
// remaining parts go through the ordinary padding process.
|
// remaining parts go through the ordinary padding process.
|
||||||
|
@ -1548,8 +1784,8 @@ impl<'a> Formatter<'a> {
|
||||||
}
|
}
|
||||||
post_padding.write(self)
|
post_padding.write(self)
|
||||||
};
|
};
|
||||||
self.fill = old_fill;
|
self.options.fill(old_fill);
|
||||||
self.align = old_align;
|
self.options.alignment(old_align);
|
||||||
ret
|
ret
|
||||||
} else {
|
} else {
|
||||||
// this is the common case and we take a shortcut
|
// this is the common case and we take a shortcut
|
||||||
|
@ -1675,7 +1911,7 @@ impl<'a> Formatter<'a> {
|
||||||
or `sign_aware_zero_pad` methods instead"
|
or `sign_aware_zero_pad` methods instead"
|
||||||
)]
|
)]
|
||||||
pub fn flags(&self) -> u32 {
|
pub fn flags(&self) -> u32 {
|
||||||
self.flags
|
self.options.get_flags()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the character used as 'fill' whenever there is alignment.
|
/// Returns the character used as 'fill' whenever there is alignment.
|
||||||
|
@ -1708,7 +1944,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn fill(&self) -> char {
|
pub fn fill(&self) -> char {
|
||||||
self.fill
|
self.options.get_fill()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a flag indicating what form of alignment was requested.
|
/// Returns a flag indicating what form of alignment was requested.
|
||||||
|
@ -1743,12 +1979,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
|
#[stable(feature = "fmt_flags_align", since = "1.28.0")]
|
||||||
pub fn align(&self) -> Option<Alignment> {
|
pub fn align(&self) -> Option<Alignment> {
|
||||||
match self.align {
|
self.options.get_alignment()
|
||||||
rt::Alignment::Left => Some(Alignment::Left),
|
|
||||||
rt::Alignment::Right => Some(Alignment::Right),
|
|
||||||
rt::Alignment::Center => Some(Alignment::Center),
|
|
||||||
rt::Alignment::Unknown => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the optionally specified integer width that the output should be.
|
/// Returns the optionally specified integer width that the output should be.
|
||||||
|
@ -1778,7 +2009,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn width(&self) -> Option<usize> {
|
pub fn width(&self) -> Option<usize> {
|
||||||
self.width
|
self.options.get_width()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the optionally specified precision for numeric types.
|
/// Returns the optionally specified precision for numeric types.
|
||||||
|
@ -1809,7 +2040,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn precision(&self) -> Option<usize> {
|
pub fn precision(&self) -> Option<usize> {
|
||||||
self.precision
|
self.options.get_precision()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if the `+` flag was specified.
|
/// Determines if the `+` flag was specified.
|
||||||
|
@ -1841,7 +2072,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn sign_plus(&self) -> bool {
|
pub fn sign_plus(&self) -> bool {
|
||||||
self.flags & (1 << rt::Flag::SignPlus as u32) != 0
|
self.options.get_sign() == Some(Sign::Plus)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if the `-` flag was specified.
|
/// Determines if the `-` flag was specified.
|
||||||
|
@ -1870,7 +2101,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn sign_minus(&self) -> bool {
|
pub fn sign_minus(&self) -> bool {
|
||||||
self.flags & (1 << rt::Flag::SignMinus as u32) != 0
|
self.options.get_sign() == Some(Sign::Minus)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if the `#` flag was specified.
|
/// Determines if the `#` flag was specified.
|
||||||
|
@ -1898,7 +2129,7 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn alternate(&self) -> bool {
|
pub fn alternate(&self) -> bool {
|
||||||
self.flags & (1 << rt::Flag::Alternate as u32) != 0
|
self.options.get_alternate()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines if the `0` flag was specified.
|
/// Determines if the `0` flag was specified.
|
||||||
|
@ -1924,17 +2155,17 @@ impl<'a> Formatter<'a> {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
#[stable(feature = "fmt_flags", since = "1.5.0")]
|
||||||
pub fn sign_aware_zero_pad(&self) -> bool {
|
pub fn sign_aware_zero_pad(&self) -> bool {
|
||||||
self.flags & (1 << rt::Flag::SignAwareZeroPad as u32) != 0
|
self.options.get_sign_aware_zero_pad()
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Decide what public API we want for these two flags.
|
// FIXME: Decide what public API we want for these two flags.
|
||||||
// https://github.com/rust-lang/rust/issues/48584
|
// https://github.com/rust-lang/rust/issues/48584
|
||||||
fn debug_lower_hex(&self) -> bool {
|
fn debug_lower_hex(&self) -> bool {
|
||||||
self.flags & (1 << rt::Flag::DebugLowerHex as u32) != 0
|
self.options.debug_as_hex == Some(DebugAsHex::Lower)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn debug_upper_hex(&self) -> bool {
|
fn debug_upper_hex(&self) -> bool {
|
||||||
self.flags & (1 << rt::Flag::DebugUpperHex as u32) != 0
|
self.options.debug_as_hex == Some(DebugAsHex::Upper)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a [`DebugStruct`] builder designed to assist with creation of
|
/// Creates a [`DebugStruct`] builder designed to assist with creation of
|
||||||
|
@ -2350,6 +2581,18 @@ impl<'a> Formatter<'a> {
|
||||||
pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> {
|
pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, 'a> {
|
||||||
builders::debug_map_new(self)
|
builders::debug_map_new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the sign of this formatter (`+` or `-`).
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn sign(&self) -> Option<Sign> {
|
||||||
|
self.options.get_sign()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the formatting options this formatter corresponds to.
|
||||||
|
#[unstable(feature = "formatting_options", issue = "118117")]
|
||||||
|
pub fn options(&self) -> FormattingOptions {
|
||||||
|
self.options
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[stable(since = "1.2.0", feature = "formatter_write")]
|
#[stable(since = "1.2.0", feature = "formatter_write")]
|
||||||
|
@ -2527,25 +2770,27 @@ impl<T: ?Sized> Pointer for *const T {
|
||||||
/// [problematic]: https://github.com/rust-lang/rust/issues/95489
|
/// [problematic]: https://github.com/rust-lang/rust/issues/95489
|
||||||
pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result {
|
pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result {
|
||||||
let old_width = f.width();
|
let old_width = f.width();
|
||||||
let old_flags = f.flags();
|
let old_alternate = f.alternate();
|
||||||
|
let old_zero_pad = f.sign_aware_zero_pad();
|
||||||
|
|
||||||
// The alternate flag is already treated by LowerHex as being special-
|
// The alternate flag is already treated by LowerHex as being special-
|
||||||
// it denotes whether to prefix with 0x. We use it to work out whether
|
// it denotes whether to prefix with 0x. We use it to work out whether
|
||||||
// or not to zero extend, and then unconditionally set it to get the
|
// or not to zero extend, and then unconditionally set it to get the
|
||||||
// prefix.
|
// prefix.
|
||||||
if f.alternate() {
|
if f.alternate() {
|
||||||
f.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32);
|
f.options.sign_aware_zero_pad(true);
|
||||||
|
|
||||||
if f.width().is_none() {
|
if f.width().is_none() {
|
||||||
f.width = Some((usize::BITS / 4) as usize + 2);
|
f.options.width(Some((usize::BITS / 4) as usize + 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f.flags |= 1 << (rt::Flag::Alternate as u32);
|
f.options.alternate(true);
|
||||||
|
|
||||||
let ret = LowerHex::fmt(&ptr_addr, f);
|
let ret = LowerHex::fmt(&ptr_addr, f);
|
||||||
|
|
||||||
f.width = old_width;
|
f.options.width(old_width);
|
||||||
f.flags = old_flags;
|
f.options.alternate(old_alternate);
|
||||||
|
f.options.sign_aware_zero_pad(old_zero_pad);
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,3 +50,31 @@ fn test_maybe_uninit_short() {
|
||||||
let x = core::mem::MaybeUninit::new(0u32);
|
let x = core::mem::MaybeUninit::new(0u32);
|
||||||
assert_eq!(format!("{x:?}"), "MaybeUninit<u32>");
|
assert_eq!(format!("{x:?}"), "MaybeUninit<u32>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn formatting_options_flags() {
|
||||||
|
use core::fmt::*;
|
||||||
|
for sign in [None, Some(Sign::Plus), Some(Sign::Minus)] {
|
||||||
|
for alternate in [true, false] {
|
||||||
|
for sign_aware_zero_pad in [true, false] {
|
||||||
|
for debug_as_hex in [None, Some(DebugAsHex::Lower), Some(DebugAsHex::Upper)] {
|
||||||
|
let mut original_formatting_options = FormattingOptions::new();
|
||||||
|
original_formatting_options
|
||||||
|
.sign(sign)
|
||||||
|
.sign_aware_zero_pad(sign_aware_zero_pad)
|
||||||
|
.alternate(alternate)
|
||||||
|
.debug_as_hex(debug_as_hex);
|
||||||
|
|
||||||
|
let mut formatting_options_with_flags_set_to_self = original_formatting_options;
|
||||||
|
formatting_options_with_flags_set_to_self
|
||||||
|
.flags(formatting_options_with_flags_set_to_self.get_flags());
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
original_formatting_options, formatting_options_with_flags_set_to_self,
|
||||||
|
"Reading and setting flags changes FormattingOptions; Sign({sign:?}), Alternate({alternate:?}). DebugAsHex({debug_as_hex:?})"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#![feature(float_minimum_maximum)]
|
#![feature(float_minimum_maximum)]
|
||||||
#![feature(flt2dec)]
|
#![feature(flt2dec)]
|
||||||
#![feature(fmt_internals)]
|
#![feature(fmt_internals)]
|
||||||
|
#![feature(formatting_options)]
|
||||||
#![feature(freeze)]
|
#![feature(freeze)]
|
||||||
#![feature(future_join)]
|
#![feature(future_join)]
|
||||||
#![feature(generic_assert_internals)]
|
#![feature(generic_assert_internals)]
|
||||||
|
|
|
@ -292,6 +292,7 @@
|
||||||
#![feature(dropck_eyepatch)]
|
#![feature(dropck_eyepatch)]
|
||||||
#![feature(f128)]
|
#![feature(f128)]
|
||||||
#![feature(f16)]
|
#![feature(f16)]
|
||||||
|
#![feature(formatting_options)]
|
||||||
#![feature(if_let_guard)]
|
#![feature(if_let_guard)]
|
||||||
#![feature(intra_doc_pointers)]
|
#![feature(intra_doc_pointers)]
|
||||||
#![feature(lang_items)]
|
#![feature(lang_items)]
|
||||||
|
|
|
@ -623,7 +623,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
|
||||||
// Lazily, the first time this gets called, run the actual string formatting.
|
// Lazily, the first time this gets called, run the actual string formatting.
|
||||||
self.string.get_or_insert_with(|| {
|
self.string.get_or_insert_with(|| {
|
||||||
let mut s = String::new();
|
let mut s = String::new();
|
||||||
let mut fmt = fmt::Formatter::new(&mut s);
|
let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new());
|
||||||
let _err = fmt::Display::fmt(&inner, &mut fmt);
|
let _err = fmt::Display::fmt(&inner, &mut fmt);
|
||||||
s
|
s
|
||||||
})
|
})
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue