#string-formatting #format-string #format #string #fmt #macro #flavor

无std custom-format

为Rust提供的自定义格式化

12个版本

0.3.1 2023年7月23日
0.3.0 2022年12月20日
0.2.0 2022年7月31日
0.1.2 2022年7月24日
0.0.6 2022年7月23日

#485 in Rust模式

MIT/Apache

29KB
273

custom-format

version Minimum supported Rust version Documentation

这个crate通过提供自定义格式化宏,扩展了标准格式化语法,支持自定义格式说明符。

它使用 :(一个空格和一个冒号)作为格式说明符前的分隔符,这不是当前接受的语法,但它允许在自定义说明符之外还支持标准说明符。它还支持即使在较旧的Rust版本上,也支持格式参数捕获,因为它在缺少时手动添加了命名参数。

这个库有两种风味,对应以下功能

  • 编译时(默认启用)

    可能的自定义格式说明符集在编译时定义,因此可以在编译时检查无效的说明符。这允许库具有与使用标准库格式化特性相同的性能。

  • 运行时(默认启用)

    格式化方法在每个调用时动态检查格式说明符。这是一个较慢的版本,但它具有额外的灵活性。

文档

文档托管在docs.rs

示例

代码
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,
}

macro_rules! impl_custom_format_for_datetime {
    (match spec { $($spec:literal => $func:expr $(,)?)* }) => {
        use cfmt::compile_time::{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
impl_custom_format_for_datetime!(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.month_day),
    // 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::custom_formatter!("%m", this);
            let day = cfmt::custom_formatter!("%d", this);
            let year = cfmt::custom_formatter!("%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.next_back()) {
            // Nanoseconds with n digits (%nN)
            (Some('%'), Some('N')) => match chars.as_str().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,
//         ::custom_format::custom_formatter!("%Y", arg1),
//         ::custom_format::custom_formatter!("%m", arg1),
//         ::custom_format::custom_formatter!("%d", arg1),
//         ::custom_format::custom_formatter!("%H", arg1),
//         ::custom_format::custom_formatter!("%M", arg1),
//         ::custom_format::custom_formatter!("%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);

编译器支持

需要rustc 1.56+

许可证

本项目许可采用Apache License,版本2.0或MIT许可证。

任选其一。

除非你明确声明,否则你故意提交以供包含在本项目中的任何贡献,根据Apache-2.0许可证定义,应如上双重许可,不附加任何额外条款或条件。

依赖项

~195KB