From bfd86b1338c3e048d61a5ed9ef5104b0ad58f60b Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 24 Sep 2022 20:47:06 +0200 Subject: [PATCH] std --- src/args.rs | 9 + src/formatter.rs | 503 +++++++++++++++++++++++++++++++ src/lib.rs | 63 +--- src/opts.rs | 4 + src/rust_core_impl/aggregated.rs | 21 ++ src/rust_core_impl/mod.rs | 1 + tests/base.rs | 21 ++ tests/num.rs | 224 ++++++++++++++ 8 files changed, 795 insertions(+), 51 deletions(-) create mode 100644 src/formatter.rs create mode 100644 src/rust_core_impl/aggregated.rs create mode 100644 tests/base.rs create mode 100644 tests/num.rs diff --git a/src/args.rs b/src/args.rs index 196da5a..12050b9 100644 --- a/src/args.rs +++ b/src/args.rs @@ -65,6 +65,14 @@ macro_rules! traits { } } )* + + pub mod macro_exports { + pub use super::{$($name, $trait),*}; + } + + pub mod pub_exports { + pub use super::{$($name, $trait),*}; + } }; } @@ -77,4 +85,5 @@ traits!( struct UpperHexArg: trait UpperHex; struct UpperExpArg: trait UpperExp; struct LowerExpArg: trait LowerExp; + struct PointerArg: trait Pointer; ); diff --git a/src/formatter.rs b/src/formatter.rs new file mode 100644 index 0000000..266f7c9 --- /dev/null +++ b/src/formatter.rs @@ -0,0 +1,503 @@ +use crate::{Debug, FmtOpts, Result, Write}; + +pub struct Formatter { + pub(crate) buf: W, + pub(crate) opts: O, +} + +impl core::fmt::Write for Formatter { + fn write_char(&mut self, c: char) -> std::fmt::Result { + self.buf.write_char(c).map_err(|_| std::fmt::Error) + } + fn write_str(&mut self, s: &str) -> std::fmt::Result { + self.buf.write_str(s).map_err(|_| std::fmt::Error) + } +} + +impl Formatter { + pub fn new(buf: W) -> Self { + Self { buf, opts: () } + } +} + +impl Formatter { + pub fn write_char(&mut self, char: char) -> Result { + self.buf.write_char(char) + } + + pub fn write_str(&mut self, str: &str) -> Result { + self.buf.write_str(str) + } + + pub fn debug_list<'b>(&'b mut self) -> DebugList<'b, W, O> { + debug_list_new(self) + } + + pub fn debug_set<'b>(&'b mut self) -> DebugSet<'b, W, O> { + debug_set_new(self) + } + + pub fn debug_map<'b>(&'b mut self) -> DebugMap<'b, W, O> { + debug_map_new(self) + } + + fn wrap_buf<'buf, F, Wrap>(&'buf mut self, wrap: F) -> Formatter + where + F: FnOnce(&'buf mut W) -> Wrap, + { + Formatter { + buf: wrap(&mut self.buf), + opts: self.opts, + } + } +} + +impl Formatter { + pub(crate) fn wrap_with( + &mut self, + opts: &ONew, + ) -> Formatter<&mut W, ONew::ReplaceInnermost> { + Formatter { + buf: &mut self.buf, + opts: opts.override_other(self.opts), + } + } +} + +// BUILDERS +// adapted from `core` +use crate as fmt; + +struct PadAdapter<'state, 'buf, W> { + buf: &'buf mut W, + state: &'state mut PadAdapterState, +} + +struct PadAdapterState { + on_newline: bool, +} + +impl Default for PadAdapterState { + fn default() -> Self { + PadAdapterState { on_newline: true } + } +} + +impl<'state, 'buf, W> PadAdapter<'state, 'buf, W> +where + W: Write + 'buf, +{ + fn wrap<'slot, 'fmt, O: FmtOpts>( + fmt: &'fmt mut Formatter, + slot: &'slot mut Option, + state: &'state mut PadAdapterState, + ) -> fmt::Formatter<&'slot mut PadAdapter<'state, 'buf, W>, O> + where + 'fmt: 'buf + 'slot, + { + fmt.wrap_buf(move |buf| slot.insert(PadAdapter { buf, state })) + } +} + +impl fmt::Write for PadAdapter<'_, '_, W> { + fn write_str(&mut self, mut s: &str) -> fmt::Result { + while !s.is_empty() { + if self.state.on_newline { + self.buf.write_str(" ")?; + } + + let split = match s.find('\n') { + Some(pos) => { + self.state.on_newline = true; + pos + 1 + } + None => { + self.state.on_newline = false; + s.len() + } + }; + self.buf.write_str(&s[..split])?; + s = &s[split..]; + } + + Ok(()) + } +} + +#[must_use = "must eventually call `finish()` on Debug builders"] +pub struct DebugStruct<'a, W, O> { + fmt: &'a mut fmt::Formatter, + result: fmt::Result, + has_fields: bool, +} + +pub(super) fn debug_struct_new<'a, W: Write, O: FmtOpts>( + fmt: &'a mut fmt::Formatter, + name: &str, +) -> DebugStruct<'a, W, O> { + let result = fmt.write_str(name); + DebugStruct { + fmt, + result, + has_fields: false, + } +} + +impl<'a, W: Write, O: FmtOpts> DebugStruct<'a, W, O> { + pub fn field(&mut self, name: &str, value: &impl Debug) -> &mut Self { + self.result = self.result.and_then(|_| { + if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str(" {\n")?; + } + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); + writer.write_str(name)?; + writer.write_str(": ")?; + value.fmt(&mut writer)?; + writer.write_str(",\n") + } else { + let prefix = if self.has_fields { ", " } else { " { " }; + self.fmt.write_str(prefix)?; + self.fmt.write_str(name)?; + self.fmt.write_str(": ")?; + value.fmt(self.fmt) + } + }); + + self.has_fields = true; + self + } + + pub fn finish_non_exhaustive(&mut self) -> fmt::Result { + self.result = self.result.and_then(|_| { + if self.has_fields { + if self.is_pretty() { + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); + writer.write_str("..\n")?; + self.fmt.write_str("}") + } else { + self.fmt.write_str(", .. }") + } + } else { + self.fmt.write_str(" { .. }") + } + }); + self.result + } + + pub fn finish(&mut self) -> fmt::Result { + if self.has_fields { + self.result = self.result.and_then(|_| { + if self.is_pretty() { + self.fmt.write_str("}") + } else { + self.fmt.write_str(" }") + } + }); + } + self.result + } + + fn is_pretty(&self) -> bool { + self.fmt.alternate() + } +} + +#[must_use = "must eventually call `finish()` on Debug builders"] +#[allow(missing_debug_implementations)] +pub struct DebugTuple<'a, W, O> { + fmt: &'a mut fmt::Formatter, + result: fmt::Result, + fields: usize, + empty_name: bool, +} + +pub(super) fn debug_tuple_new<'a, W: Write, O: FmtOpts>( + fmt: &'a mut fmt::Formatter, + name: &str, +) -> DebugTuple<'a, W, O> { + let result = fmt.write_str(name); + DebugTuple { + fmt, + result, + fields: 0, + empty_name: name.is_empty(), + } +} + +impl<'a, W: Write, O: FmtOpts> DebugTuple<'a, W, O> { + pub fn field(&mut self, value: &impl Debug) -> &mut Self { + self.result = self.result.and_then(|_| { + if self.is_pretty() { + if self.fields == 0 { + self.fmt.write_str("(\n")?; + } + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); + value.fmt(&mut writer)?; + writer.write_str(",\n") + } else { + let prefix = if self.fields == 0 { "(" } else { ", " }; + self.fmt.write_str(prefix)?; + value.fmt(self.fmt) + } + }); + + self.fields += 1; + self + } + + pub fn finish(&mut self) -> fmt::Result { + if self.fields > 0 { + self.result = self.result.and_then(|_| { + if self.fields == 1 && self.empty_name && !self.is_pretty() { + self.fmt.write_str(",")?; + } + self.fmt.write_str(")") + }); + } + self.result + } + + fn is_pretty(&self) -> bool { + self.fmt.alternate() + } +} + +struct DebugInner<'a, W, O> { + fmt: &'a mut fmt::Formatter, + result: fmt::Result, + has_fields: bool, +} + +impl<'a, W: Write, O: FmtOpts> DebugInner<'a, W, O> { + fn entry(&mut self, entry: &impl Debug) { + self.result = self.result.and_then(|_| { + if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str("\n")?; + } + let mut slot = None; + let mut state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state); + entry.fmt(&mut writer)?; + writer.write_str(",\n") + } else { + if self.has_fields { + self.fmt.write_str(", ")? + } + entry.fmt(self.fmt) + } + }); + + self.has_fields = true; + } + + fn is_pretty(&self) -> bool { + self.fmt.alternate() + } +} + +#[must_use = "must eventually call `finish()` on Debug builders"] +#[allow(missing_debug_implementations)] +pub struct DebugSet<'a, W, O> { + inner: DebugInner<'a, W, O>, +} + +pub(super) fn debug_set_new<'a, W: Write, O: FmtOpts>( + fmt: &'a mut fmt::Formatter, +) -> DebugSet<'a, W, O> { + let result = fmt.write_str("{"); + DebugSet { + inner: DebugInner { + fmt, + result, + has_fields: false, + }, + } +} + +impl<'a, W: Write, O: FmtOpts> DebugSet<'a, W, O> { + pub fn entry(&mut self, entry: &impl Debug) -> &mut Self { + self.inner.entry(entry); + self + } + + pub fn entries(&mut self, entries: I) -> &mut Self + where + D: fmt::Debug, + I: IntoIterator, + { + for entry in entries { + self.entry(&entry); + } + self + } + + pub fn finish(&mut self) -> fmt::Result { + self.inner + .result + .and_then(|_| self.inner.fmt.write_str("}")) + } +} + +#[must_use = "must eventually call `finish()` on Debug builders"] +#[allow(missing_debug_implementations)] +pub struct DebugList<'a, W, O> { + inner: DebugInner<'a, W, O>, +} + +pub(super) fn debug_list_new<'a, W: Write, O: FmtOpts>( + fmt: &'a mut fmt::Formatter, +) -> DebugList<'a, W, O> { + let result = fmt.write_str("["); + DebugList { + inner: DebugInner { + fmt, + result, + has_fields: false, + }, + } +} + +impl<'a, W: Write, O: FmtOpts> DebugList<'a, W, O> { + pub fn entry(&mut self, entry: &impl Debug) -> &mut Self { + self.inner.entry(entry); + self + } + + pub fn entries(&mut self, entries: I) -> &mut Self + where + D: fmt::Debug, + I: IntoIterator, + { + for entry in entries { + self.entry(&entry); + } + self + } + + pub fn finish(&mut self) -> fmt::Result { + self.inner + .result + .and_then(|_| self.inner.fmt.write_str("]")) + } +} + +#[must_use = "must eventually call `finish()` on Debug builders"] +#[allow(missing_debug_implementations)] +pub struct DebugMap<'a, W, O> { + fmt: &'a mut fmt::Formatter, + result: fmt::Result, + has_fields: bool, + has_key: bool, + // The state of newlines is tracked between keys and values + state: PadAdapterState, +} + +pub(super) fn debug_map_new<'a, 'b, W: Write, O: FmtOpts>( + fmt: &'a mut fmt::Formatter, +) -> DebugMap<'a, W, O> { + let result = fmt.write_str("{"); + DebugMap { + fmt, + result, + has_fields: false, + has_key: false, + state: Default::default(), + } +} + +impl<'a, W: Write, O: FmtOpts> DebugMap<'a, W, O> { + pub fn entry(&mut self, key: &impl Debug, value: &impl Debug) -> &mut Self { + self.key(key).value(value) + } + + pub fn key(&mut self, key: &impl Debug) -> &mut Self { + self.result = self.result.and_then(|_| { + assert!( + !self.has_key, + "attempted to begin a new map entry \ + without completing the previous one" + ); + + if self.is_pretty() { + if !self.has_fields { + self.fmt.write_str("\n")?; + } + let mut slot = None; + self.state = Default::default(); + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state); + key.fmt(&mut writer)?; + writer.write_str(": ")?; + } else { + if self.has_fields { + self.fmt.write_str(", ")? + } + key.fmt(self.fmt)?; + self.fmt.write_str(": ")?; + } + + self.has_key = true; + Ok(()) + }); + + self + } + + pub fn value(&mut self, value: impl Debug) -> &mut Self { + self.result = self.result.and_then(|_| { + assert!( + self.has_key, + "attempted to format a map value before its key" + ); + + if self.is_pretty() { + let mut slot = None; + let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut self.state); + value.fmt(&mut writer)?; + writer.write_str(",\n")?; + } else { + value.fmt(self.fmt)?; + } + + self.has_key = false; + Ok(()) + }); + + self.has_fields = true; + self + } + + pub fn entries(&mut self, entries: I) -> &mut Self + where + K: fmt::Debug, + V: fmt::Debug, + I: IntoIterator, + { + for (k, v) in entries { + self.entry(&k, &v); + } + self + } + + pub fn finish(&mut self) -> fmt::Result { + self.result.and_then(|_| { + assert!( + !self.has_key, + "attempted to finish a map with a partial entry" + ); + + self.fmt.write_str("}") + }) + } + + fn is_pretty(&self) -> bool { + self.fmt.alternate() + } +} diff --git a/src/lib.rs b/src/lib.rs index 325bdf5..f7cbb57 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![allow(dead_code)] mod args; +mod formatter; mod opts; mod rust_core_impl; mod write; @@ -13,18 +14,23 @@ macro_rules! format_args { } pub use crate::{ - args::{Arguments, Binary, Debug, Display, LowerExp, LowerHex, Octal, UpperExp, UpperHex}, + args::{pub_exports::*, Arguments}, + formatter::Formatter, opts::FmtOpts, }; pub type Result = std::result::Result<(), Error>; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub struct Error; pub trait Write { fn write_str(&mut self, str: &str) -> Result; - fn write_char(&mut self, char: char) -> Result; + + fn write_char(&mut self, char: char) -> Result { + let mut buf = [0; 4]; + self.write_str(char.encode_utf8(&mut buf)) + } } impl Write for String { @@ -49,48 +55,6 @@ impl Write for &mut W { } } -pub struct Formatter { - buf: W, - opts: O, -} - -impl core::fmt::Write for Formatter { - fn write_char(&mut self, c: char) -> std::fmt::Result { - self.buf.write_char(c).map_err(|_| std::fmt::Error) - } - fn write_str(&mut self, s: &str) -> std::fmt::Result { - self.buf.write_str(s).map_err(|_| std::fmt::Error) - } -} - -impl Formatter { - fn new(buf: W) -> Self { - Self { buf, opts: () } - } -} - -impl Formatter { - pub fn write_char(&mut self, char: char) -> Result { - self.buf.write_char(char) - } - - pub fn write_str(&mut self, str: &str) -> Result { - self.buf.write_str(str) - } -} - -impl Formatter { - fn wrap_with<'opt, ONew: FmtOpts>( - &mut self, - opts: &ONew, - ) -> Formatter<&mut W, ONew::ReplaceInnermost> { - Formatter { - buf: &mut self.buf, - opts: opts.override_other(self.opts), - } - } -} - pub mod helpers { use crate::{Arguments, Formatter, Result, Write}; @@ -108,15 +72,12 @@ pub mod helpers { /// Not part of the public API. #[doc(hidden)] -mod _private { +pub mod _private { pub use mono_fmt_macro::__format_args; pub use crate::{ - args::{ - BinaryArg, DebugArg, DisplayArg, LowerExpArg, LowerHexArg, OctalArg, Str, UpperExpArg, - UpperHexArg, - }, - opts::{WithAlign, WithAlternate, WithFill, WithWidth}, + args::{macro_exports::*, Str}, + opts::exports::*, }; } diff --git a/src/opts.rs b/src/opts.rs index f4f9185..2266aa9 100644 --- a/src/opts.rs +++ b/src/opts.rs @@ -121,6 +121,10 @@ macro_rules! options { } } )* + + pub mod exports { + pub use super::{$($with_name),*}; + } }; } diff --git a/src/rust_core_impl/aggregated.rs b/src/rust_core_impl/aggregated.rs new file mode 100644 index 0000000..4621ba5 --- /dev/null +++ b/src/rust_core_impl/aggregated.rs @@ -0,0 +1,21 @@ +//! A bunch of impls copied from all over the place + +use crate::{Debug, FmtOpts, Formatter, Result, Write}; + +impl Debug for &T { + fn fmt(&self, f: &mut Formatter) -> Result { + ::fmt(&self, f) + } +} + +impl Debug for [T; N] { + fn fmt(&self, f: &mut Formatter) -> Result { + <[T] as Debug>::fmt(&&self[..], f) + } +} + +impl Debug for [T] { + fn fmt(&self, f: &mut Formatter) -> Result { + f.debug_list().entries(self.iter()).finish() + } +} diff --git a/src/rust_core_impl/mod.rs b/src/rust_core_impl/mod.rs index 8957941..05ac7b3 100644 --- a/src/rust_core_impl/mod.rs +++ b/src/rust_core_impl/mod.rs @@ -1,5 +1,6 @@ //! Copied modified stuff from core +mod aggregated; mod num; use crate::{opts::Alignment, Error, FmtOpts, Formatter, Result, Write}; diff --git a/tests/base.rs b/tests/base.rs new file mode 100644 index 0000000..6385792 --- /dev/null +++ b/tests/base.rs @@ -0,0 +1,21 @@ +use mono_fmt::{FmtOpts, Write}; + +#[macro_use] +extern crate mono_fmt; + +#[test] +fn test_format_flags() { + // No residual flags left by pointer formatting + let p = "".as_ptr(); + assert_eq!(format!("{:p} {:x}", p, 16), format!("{p:p} 10")); + + assert_eq!(format!("{: >3}", 'a'), " a"); +} + +#[test] +fn test_pointer_formats_data_pointer() { + let b: &[u8] = b""; + let s: &str = ""; + assert_eq!(format!("{s:p}"), format!("{:p}", s.as_ptr())); + assert_eq!(format!("{b:p}"), format!("{:p}", b.as_ptr())); +} diff --git a/tests/num.rs b/tests/num.rs new file mode 100644 index 0000000..06f611e --- /dev/null +++ b/tests/num.rs @@ -0,0 +1,224 @@ +#[macro_use] +extern crate mono_fmt; + +#[test] +fn test_format_int() { + // Formatting integers should select the right implementation based off + // the type of the argument. Also, hex/octal/binary should be defined + // for integers, but they shouldn't emit the negative sign. + assert_eq!(format!("{}", 1isize), "1"); + assert_eq!(format!("{}", 1i8), "1"); + assert_eq!(format!("{}", 1i16), "1"); + assert_eq!(format!("{}", 1i32), "1"); + assert_eq!(format!("{}", 1i64), "1"); + assert_eq!(format!("{}", -1isize), "-1"); + assert_eq!(format!("{}", -1i8), "-1"); + assert_eq!(format!("{}", -1i16), "-1"); + assert_eq!(format!("{}", -1i32), "-1"); + assert_eq!(format!("{}", -1i64), "-1"); + assert_eq!(format!("{:?}", 1isize), "1"); + assert_eq!(format!("{:?}", 1i8), "1"); + assert_eq!(format!("{:?}", 1i16), "1"); + assert_eq!(format!("{:?}", 1i32), "1"); + assert_eq!(format!("{:?}", 1i64), "1"); + assert_eq!(format!("{:b}", 1isize), "1"); + assert_eq!(format!("{:b}", 1i8), "1"); + assert_eq!(format!("{:b}", 1i16), "1"); + assert_eq!(format!("{:b}", 1i32), "1"); + assert_eq!(format!("{:b}", 1i64), "1"); + assert_eq!(format!("{:x}", 1isize), "1"); + assert_eq!(format!("{:x}", 1i8), "1"); + assert_eq!(format!("{:x}", 1i16), "1"); + assert_eq!(format!("{:x}", 1i32), "1"); + assert_eq!(format!("{:x}", 1i64), "1"); + assert_eq!(format!("{:X}", 1isize), "1"); + assert_eq!(format!("{:X}", 1i8), "1"); + assert_eq!(format!("{:X}", 1i16), "1"); + assert_eq!(format!("{:X}", 1i32), "1"); + assert_eq!(format!("{:X}", 1i64), "1"); + assert_eq!(format!("{:o}", 1isize), "1"); + assert_eq!(format!("{:o}", 1i8), "1"); + assert_eq!(format!("{:o}", 1i16), "1"); + assert_eq!(format!("{:o}", 1i32), "1"); + assert_eq!(format!("{:o}", 1i64), "1"); + assert_eq!(format!("{:e}", 1isize), "1e0"); + assert_eq!(format!("{:e}", 1i8), "1e0"); + assert_eq!(format!("{:e}", 1i16), "1e0"); + assert_eq!(format!("{:e}", 1i32), "1e0"); + assert_eq!(format!("{:e}", 1i64), "1e0"); + assert_eq!(format!("{:E}", 1isize), "1E0"); + assert_eq!(format!("{:E}", 1i8), "1E0"); + assert_eq!(format!("{:E}", 1i16), "1E0"); + assert_eq!(format!("{:E}", 1i32), "1E0"); + assert_eq!(format!("{:E}", 1i64), "1E0"); + + assert_eq!(format!("{}", 1usize), "1"); + assert_eq!(format!("{}", 1u8), "1"); + assert_eq!(format!("{}", 1u16), "1"); + assert_eq!(format!("{}", 1u32), "1"); + assert_eq!(format!("{}", 1u64), "1"); + assert_eq!(format!("{:?}", 1usize), "1"); + assert_eq!(format!("{:?}", 1u8), "1"); + assert_eq!(format!("{:?}", 1u16), "1"); + assert_eq!(format!("{:?}", 1u32), "1"); + assert_eq!(format!("{:?}", 1u64), "1"); + assert_eq!(format!("{:b}", 1usize), "1"); + assert_eq!(format!("{:b}", 1u8), "1"); + assert_eq!(format!("{:b}", 1u16), "1"); + assert_eq!(format!("{:b}", 1u32), "1"); + assert_eq!(format!("{:b}", 1u64), "1"); + assert_eq!(format!("{:x}", 1usize), "1"); + assert_eq!(format!("{:x}", 1u8), "1"); + assert_eq!(format!("{:x}", 1u16), "1"); + assert_eq!(format!("{:x}", 1u32), "1"); + assert_eq!(format!("{:x}", 1u64), "1"); + assert_eq!(format!("{:X}", 1usize), "1"); + assert_eq!(format!("{:X}", 1u8), "1"); + assert_eq!(format!("{:X}", 1u16), "1"); + assert_eq!(format!("{:X}", 1u32), "1"); + assert_eq!(format!("{:X}", 1u64), "1"); + assert_eq!(format!("{:o}", 1usize), "1"); + assert_eq!(format!("{:o}", 1u8), "1"); + assert_eq!(format!("{:o}", 1u16), "1"); + assert_eq!(format!("{:o}", 1u32), "1"); + assert_eq!(format!("{:o}", 1u64), "1"); + assert_eq!(format!("{:e}", 1u8), "1e0"); + assert_eq!(format!("{:e}", 1u16), "1e0"); + assert_eq!(format!("{:e}", 1u32), "1e0"); + assert_eq!(format!("{:e}", 1u64), "1e0"); + assert_eq!(format!("{:E}", 1u8), "1E0"); + assert_eq!(format!("{:E}", 1u16), "1E0"); + assert_eq!(format!("{:E}", 1u32), "1E0"); + assert_eq!(format!("{:E}", 1u64), "1E0"); + + // Test a larger number + assert_eq!(format!("{:b}", 55), "110111"); + assert_eq!(format!("{:o}", 55), "67"); + assert_eq!(format!("{}", 55), "55"); + assert_eq!(format!("{:x}", 55), "37"); + assert_eq!(format!("{:X}", 55), "37"); + assert_eq!(format!("{:e}", 55), "5.5e1"); + assert_eq!(format!("{:E}", 55), "5.5E1"); + assert_eq!(format!("{:e}", 10000000000u64), "1e10"); + assert_eq!(format!("{:E}", 10000000000u64), "1E10"); + assert_eq!(format!("{:e}", 10000000001u64), "1.0000000001e10"); + assert_eq!(format!("{:E}", 10000000001u64), "1.0000000001E10"); +} + +#[test] +fn test_format_int_exp_limits() { + assert_eq!(format!("{:e}", i8::MIN), "-1.28e2"); + assert_eq!(format!("{:e}", i8::MAX), "1.27e2"); + assert_eq!(format!("{:e}", i16::MIN), "-3.2768e4"); + assert_eq!(format!("{:e}", i16::MAX), "3.2767e4"); + assert_eq!(format!("{:e}", i32::MIN), "-2.147483648e9"); + assert_eq!(format!("{:e}", i32::MAX), "2.147483647e9"); + assert_eq!(format!("{:e}", i64::MIN), "-9.223372036854775808e18"); + assert_eq!(format!("{:e}", i64::MAX), "9.223372036854775807e18"); + assert_eq!(format!("{:e}", i128::MIN), "-1.70141183460469231731687303715884105728e38"); + assert_eq!(format!("{:e}", i128::MAX), "1.70141183460469231731687303715884105727e38"); + + assert_eq!(format!("{:e}", u8::MAX), "2.55e2"); + assert_eq!(format!("{:e}", u16::MAX), "6.5535e4"); + assert_eq!(format!("{:e}", u32::MAX), "4.294967295e9"); + assert_eq!(format!("{:e}", u64::MAX), "1.8446744073709551615e19"); + assert_eq!(format!("{:e}", u128::MAX), "3.40282366920938463463374607431768211455e38"); +} + +#[test] +fn test_format_int_exp_precision() { + //test adding precision + assert_eq!(format!("{:.10e}", i8::MIN), "-1.2800000000e2"); + assert_eq!(format!("{:.10e}", i16::MIN), "-3.2768000000e4"); + assert_eq!(format!("{:.10e}", i32::MIN), "-2.1474836480e9"); + assert_eq!(format!("{:.20e}", i64::MIN), "-9.22337203685477580800e18"); + assert_eq!(format!("{:.40e}", i128::MIN), "-1.7014118346046923173168730371588410572800e38"); + + //test rounding + assert_eq!(format!("{:.1e}", i8::MIN), "-1.3e2"); + assert_eq!(format!("{:.1e}", i16::MIN), "-3.3e4"); + assert_eq!(format!("{:.1e}", i32::MIN), "-2.1e9"); + assert_eq!(format!("{:.1e}", i64::MIN), "-9.2e18"); + assert_eq!(format!("{:.1e}", i128::MIN), "-1.7e38"); + + //test huge precision + assert_eq!(format!("{:.1000e}", 1), format!("1.{}e0", "0".repeat(1000))); + //test zero precision + assert_eq!(format!("{:.0e}", 1), format!("1e0",)); + assert_eq!(format!("{:.0e}", 35), format!("4e1",)); + + //test padding with precision (and sign) + assert_eq!(format!("{:+10.3e}", 1), " +1.000e0"); +} + +#[test] +fn test_format_int_zero() { + assert_eq!(format!("{}", 0), "0"); + assert_eq!(format!("{:?}", 0), "0"); + assert_eq!(format!("{:b}", 0), "0"); + assert_eq!(format!("{:o}", 0), "0"); + assert_eq!(format!("{:x}", 0), "0"); + assert_eq!(format!("{:X}", 0), "0"); + assert_eq!(format!("{:e}", 0), "0e0"); + assert_eq!(format!("{:E}", 0), "0E0"); + + assert_eq!(format!("{}", 0u32), "0"); + assert_eq!(format!("{:?}", 0u32), "0"); + assert_eq!(format!("{:b}", 0u32), "0"); + assert_eq!(format!("{:o}", 0u32), "0"); + assert_eq!(format!("{:x}", 0u32), "0"); + assert_eq!(format!("{:X}", 0u32), "0"); + assert_eq!(format!("{:e}", 0u32), "0e0"); + assert_eq!(format!("{:E}", 0u32), "0E0"); +} + +#[test] +fn test_format_int_flags() { + assert_eq!(format!("{:3}", 1), " 1"); + assert_eq!(format!("{:>3}", 1), " 1"); + assert_eq!(format!("{:>+3}", 1), " +1"); + assert_eq!(format!("{:<3}", 1), "1 "); + assert_eq!(format!("{:#}", 1), "1"); + assert_eq!(format!("{:#x}", 10), "0xa"); + assert_eq!(format!("{:#X}", 10), "0xA"); + assert_eq!(format!("{:#5x}", 10), " 0xa"); + assert_eq!(format!("{:#o}", 10), "0o12"); + assert_eq!(format!("{:08x}", 10), "0000000a"); + assert_eq!(format!("{:8x}", 10), " a"); + assert_eq!(format!("{:<8x}", 10), "a "); + assert_eq!(format!("{:>8x}", 10), " a"); + assert_eq!(format!("{:#08x}", 10), "0x00000a"); + assert_eq!(format!("{:08}", -10), "-0000010"); + assert_eq!(format!("{:x}", !0u8), "ff"); + assert_eq!(format!("{:X}", !0u8), "FF"); + assert_eq!(format!("{:b}", !0u8), "11111111"); + assert_eq!(format!("{:o}", !0u8), "377"); + assert_eq!(format!("{:#x}", !0u8), "0xff"); + assert_eq!(format!("{:#X}", !0u8), "0xFF"); + assert_eq!(format!("{:#b}", !0u8), "0b11111111"); + assert_eq!(format!("{:#o}", !0u8), "0o377"); +} + +#[test] +fn test_format_int_sign_padding() { + assert_eq!(format!("{:+5}", 1), " +1"); + assert_eq!(format!("{:+5}", -1), " -1"); + assert_eq!(format!("{:05}", 1), "00001"); + assert_eq!(format!("{:05}", -1), "-0001"); + assert_eq!(format!("{:+05}", 1), "+0001"); + assert_eq!(format!("{:+05}", -1), "-0001"); +} + +#[test] +fn test_format_int_twos_complement() { + assert_eq!(format!("{}", i8::MIN), "-128"); + assert_eq!(format!("{}", i16::MIN), "-32768"); + assert_eq!(format!("{}", i32::MIN), "-2147483648"); + assert_eq!(format!("{}", i64::MIN), "-9223372036854775808"); +} + +#[test] +fn test_format_debug_hex() { + assert_eq!(format!("{:02x?}", b"Foo\0"), "[46, 6f, 6f, 00]"); + assert_eq!(format!("{:02X?}", b"Foo\0"), "[46, 6F, 6F, 00]"); +}