rt_format/lib.rs
1#![warn(missing_docs)]
2
3//! Fully-runtime equivalent of the `format!` macro.
4//!
5//! Allows formatting strings like the `format!` macro, with the formatting string and the arguments
6//! provided at runtime. This crate supports all the formatting features of the `format!` macro,
7//! except for the fill character.
8//!
9//! # Examples
10//!
11//! ```
12//! use rt_format::{Format, FormatArgument, ParsedFormat, Specifier};
13//! use std::cmp::PartialEq;
14//! use std::fmt;
15//!
16//! #[derive(Debug, PartialEq)]
17//! pub enum Variant {
18//! Int(i32),
19//! Float(f64),
20//! }
21//!
22//! impl FormatArgument for Variant {
23//! fn supports_format(&self, spec: &Specifier) -> bool {
24//! match self {
25//! Self::Int(_) => true,
26//! Self::Float(_) => match spec.format {
27//! Format::Display | Format::Debug | Format::LowerExp | Format::UpperExp => true,
28//! _ => false,
29//! },
30//! }
31//! }
32//!
33//! fn fmt_display(&self, f: &mut fmt::Formatter) -> fmt::Result {
34//! match self {
35//! Self::Int(val) => fmt::Display::fmt(&val, f),
36//! Self::Float(val) => fmt::Display::fmt(&val, f),
37//! }
38//! }
39//!
40//! fn fmt_debug(&self, f: &mut fmt::Formatter) -> fmt::Result {
41//! fmt::Debug::fmt(self, f)
42//! }
43//!
44//! fn fmt_octal(&self, f: &mut fmt::Formatter) -> fmt::Result {
45//! match self {
46//! Self::Int(val) => fmt::Octal::fmt(&val, f),
47//! _ => Err(fmt::Error),
48//! }
49//! }
50//!
51//! fn fmt_lower_hex(&self, f: &mut fmt::Formatter) -> fmt::Result {
52//! match self {
53//! Self::Int(val) => fmt::LowerHex::fmt(&val, f),
54//! _ => Err(fmt::Error),
55//! }
56//! }
57//!
58//! fn fmt_upper_hex(&self, f: &mut fmt::Formatter) -> fmt::Result {
59//! match self {
60//! Self::Int(val) => fmt::UpperHex::fmt(&val, f),
61//! _ => Err(fmt::Error),
62//! }
63//! }
64//!
65//! fn fmt_binary(&self, f: &mut fmt::Formatter) -> fmt::Result {
66//! match self {
67//! Self::Int(val) => fmt::Binary::fmt(&val, f),
68//! _ => Err(fmt::Error),
69//! }
70//! }
71//!
72//! fn fmt_lower_exp(&self, f: &mut fmt::Formatter) -> fmt::Result {
73//! match self {
74//! Self::Int(val) => fmt::LowerExp::fmt(&val, f),
75//! Self::Float(val) => fmt::LowerExp::fmt(&val, f),
76//! }
77//! }
78//!
79//! fn fmt_upper_exp(&self, f: &mut fmt::Formatter) -> fmt::Result {
80//! match self {
81//! Self::Int(val) => fmt::UpperExp::fmt(&val, f),
82//! Self::Float(val) => fmt::UpperExp::fmt(&val, f),
83//! }
84//! }
85//!
86//! fn to_usize(&self) -> Result<usize, ()> {
87//! use std::convert::TryInto;
88//! match self {
89//! Variant::Int(val) => (*val).try_into().map_err(|_| ()),
90//! Variant::Float(_) => Err(()),
91//! }
92//! }
93//! }
94//!
95//! fn main() {
96//! use std::collections::HashMap;
97//!
98//! let pos_args = [Variant::Int(42), Variant::Int(5)];
99//!
100//! let mut named_args = HashMap::new();
101//! named_args.insert("foo".to_string(), Variant::Float(42.042));
102//!
103//! let args = ParsedFormat::parse("{:#x} [{0:<5}] {foo:.1$}", &pos_args, &named_args).unwrap();
104//! assert_eq!("0x2a [42 ] 42.04200", format!("{}", args));
105//! }
106//! ```
107
108#[macro_use]
109mod codegen;
110
111pub mod argument;
112pub mod parser;
113
114use std::cmp::PartialEq;
115use std::convert::TryFrom;
116use std::fmt;
117
118pub use crate::argument::{FormatArgument, NoNamedArguments, NoPositionalArguments};
119pub use crate::parser::{ParsedFormat, Substitution};
120
121generate_code! {
122 /// Specifies the alignment of an argument with a specific width.
123 align: Align {
124 None => "",
125 Left => "<",
126 Center => "^",
127 Right => ">",
128 }
129
130 /// Specifies whether the sign of a numeric argument should always be emitted.
131 sign: Sign {
132 Default => "",
133 Always => "+",
134 }
135
136 /// Specifies whether to use the alternate representation for certain formats.
137 repr: Repr {
138 Default => "",
139 Alt => "#",
140 }
141
142 /// Specifies whether a numeric argument with specific width should be padded with spaces or
143 /// zeroes.
144 pad: Pad {
145 Space => "",
146 Zero => "0",
147 }
148
149 /// Specifies whether an argument should be padded to a specific width.
150 width: Width {
151 Auto => "",
152 AtLeast { width: usize } => "width$",
153 }
154
155 /// Specifies whether an argument should be formatted with a specific precision.
156 precision: Precision {
157 Auto => "",
158 Exactly { precision: usize } => ".precision$",
159 }
160
161 /// Specifies how to format an argument.
162 format: Format {
163 Display => "",
164 Debug => "?",
165 Octal => "o",
166 LowerHex => "x",
167 UpperHex => "X",
168 Binary => "b",
169 LowerExp => "e",
170 UpperExp => "E",
171 }
172}
173
174impl fmt::Display for Width {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 match self {
177 Width::Auto => Ok(()),
178 Width::AtLeast { width } => write!(f, "{}", width),
179 }
180 }
181}
182
183impl fmt::Display for Precision {
184 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185 match self {
186 Precision::Auto => Ok(()),
187 Precision::Exactly { precision } => write!(f, ".{}", precision),
188 }
189 }
190}