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}