custom-format

version Minimum supported Rust version Documentation

This crate extends the standard formatting syntax with custom format specifiers, by providing custom formatting macros.

It uses : (a space and a colon) as a separator before the format specifier, which is not a syntax currently accepted and allows supporting standard specifiers in addition to custom specifiers. It also supports format args capture even on older versions of Rust, since it manually adds the named parameter if missing.

This library comes in two flavors, corresponding to the following features:

Documentation

Documentation is hosted on docs.rs.

Example

Code

```rust use custom_format as cfmt;

use core::fmt;

pub struct DateTime { year: i32, month: u8, month_day: u8, hour: u8, minute: u8, second: u8, nanoseconds: u32, }

macrorules! implcustomformatfordatetime { (match spec { $($spec:literal => $func:expr $(,)?)* }) => { use cfmt::compiletime::{spec, CustomFormat}; $( impl CustomFormat<{ spec($spec) }> for DateTime { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ($func as fn(&Self, &mut fmt::Formatter) -> fmt::Result)(self, f) } } )* }; }

// Static format specifiers, checked at compile-time implcustomformatfordatetime!(match spec { // Year with pad for at least 4 digits "%Y" => |this, f| write!(f, "{:04}", this.year), // Year % 100 (00..99) "%y" => |this, f| write!(f, "{:02}", (this.year % 100).abs()), // Month of the year, zero-padded (01..12) "%m" => |this, f| write!(f, "{:02}", this.month), // Day of the month, zero-padded (01..31) "%d" => |this, f| write!(f, "{:02}", this.monthday), // Hour of the day, 24-hour clock, zero-padded (00..23) "%H" => |this, f| write!(f, "{:02}", this.hour), // Minute of the hour (00..59) "%M" => |this, f| write!(f, "{:02}", this.minute), // Second of the minute (00..60) "%S" => |this, f| write!(f, "{:02}", this.second), // Date (%m/%d/%y) "%D" => { |this, f| { let month = cfmt::customformatter!("%m", this); let day = cfmt::customformatter!("%d", this); let year = cfmt::customformatter!("%y", this); write!(f, "{}/{}/{}", month, day, year) } } });

// Dynamic format specifiers, checked at runtime impl cfmt::runtime::CustomFormat for DateTime { fn fmt(&self, f: &mut fmt::Formatter, spec: &str) -> fmt::Result { let mut chars = spec.chars(); match (chars.next(), chars.nextback()) { // Nanoseconds with n digits (%nN) (Some('%'), Some('N')) => match chars.asstr().parse() { Ok(n) if n > 0 => { if n <= 9 { write!(f, "{:0width$}", self.nanoseconds / 10u32.pow(9 - n as u32), width = n) } else { write!(f, "{:09}{:0width$}", self.nanoseconds, 0, width = n - 9) } } _ => Err(fmt::Error), }, _ => Err(fmt::Error), } } }

let dt = DateTime { year: 1836, month: 5, month_day: 18, hour: 23, minute: 45, second: 54, nanoseconds: 123456789, };

// Expands to: // // match (&("DateTime"), &dt) { // (arg0, arg1) => ::std::println!( // "The {0:?} is: {1}-{2}-{3} {4}:{5}:{6}.{7}", // arg0, // ::customformat::customformatter!("%Y", arg1), // ::customformat::customformatter!("%m", arg1), // ::customformat::customformatter!("%d", arg1), // ::customformat::customformatter!("%H", arg1), // ::customformat::customformatter!("%M", arg1), // ::customformat::customformatter!("%S", arg1), // ::custom_format::runtime::CustomFormatter::new("%6N", arg1) // ), // } // // Output: The "DateTime" is: 1836-05-18 23:45:54.123456 // // The custom format specifier is interpreted as a compile-time specifier by default, // or as a runtime specifier if it is inside "<>". cfmt::println!( "The {ty:?} is: {dt :%Y}-{dt :%m}-{dt :%d} {dt :%H}:{dt :%M}:{dt :%S}.{dt :<%6N>}", ty = "DateTime", );

// Compile-time error since "%h" is not a valid format specifier // cfmt::println!("{0 :%h}", dt);

// Panic at runtime since "%h" is not a valid format specifier // cfmt::println!("{0 :<%h>}", dt); ```

Compiler support

Requires rustc 1.54+.

License

This project is licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.