1 //! Parsing and formatting various types.
2
3 /// Pad a given value if requested.
4 macro_rules! pad {
5 ($f:ident, $padding:ident, $width:literal, $value:expr) => {
6 match $padding {
7 Padding::None => write!($f, "{}", $value),
8 Padding::Space => write!($f, concat!("{:", stringify!($width), "}"), $value),
9 Padding::Zero => write!($f, concat!("{:0", stringify!($width), "}"), $value),
10 }
11 };
12 }
13
14 pub(crate) mod date;
15 pub(crate) mod deferred_format;
16 #[allow(clippy::module_inception)]
17 pub(crate) mod format;
18 pub(crate) mod offset;
19 pub(crate) mod parse;
20 pub(crate) mod parse_items;
21 pub(crate) mod time;
22 pub(crate) mod well_known;
23
24 use crate::internal_prelude::*;
25 use core::fmt::{self, Formatter};
26 pub(crate) use deferred_format::DeferredFormat;
27 #[allow(unreachable_pub)] // rust-lang/rust#64762
28 pub use format::Format;
29 #[allow(unreachable_pub)] // rust-lang/rust#64762
30 pub use parse::ParseError;
31 pub(crate) use parse::{parse, ParseResult, ParsedItems};
32 pub(crate) use parse_items::{parse_fmt_string, try_parse_fmt_string};
33
34 /// Checks if a user-provided formatting string is valid. If it isn't, a
35 /// description of the error is returned.
36 #[inline(always)]
validate_format_string(s: impl AsRef<str>) -> Result<(), String>37 pub fn validate_format_string(s: impl AsRef<str>) -> Result<(), String> {
38 try_parse_fmt_string(s.as_ref()).map(|_| ())
39 }
40
41 /// The type of padding to use when formatting.
42 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
43 pub(crate) enum Padding {
44 /// No padding. Minimizes width.
45 None,
46 /// Pad to the requisite width using spaces.
47 Space,
48 /// Pad to the requisite width using zeros.
49 Zero,
50 }
51
52 /// Specifiers are similar to C's `strftime`, with some omissions and changes.
53 ///
54 /// See the table in `lib.rs` for a description of each specifier (and
55 /// equivalences for combination specifiers).
56 #[allow(
57 non_snake_case,
58 non_camel_case_types,
59 clippy::missing_docs_in_private_items
60 )]
61 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
62 pub(crate) enum Specifier {
63 a,
64 A,
65 b,
66 B,
67 c,
68 C { padding: Padding },
69 d { padding: Padding },
70 D,
71 F,
72 g { padding: Padding },
73 G { padding: Padding },
74 H { padding: Padding },
75 I { padding: Padding },
76 j { padding: Padding },
77 m { padding: Padding },
78 M { padding: Padding },
79 N,
80 p,
81 P,
82 r,
83 R,
84 S { padding: Padding },
85 T,
86 u,
87 U { padding: Padding },
88 V { padding: Padding },
89 w,
90 W { padding: Padding },
91 y { padding: Padding },
92 Y { padding: Padding },
93 z,
94 }
95
96 /// Given all the information necessary, write the provided specifier to the
97 /// formatter.
98 #[inline]
format_specifier( f: &mut Formatter<'_>, date: Option<Date>, time: Option<Time>, offset: Option<UtcOffset>, specifier: Specifier, ) -> fmt::Result99 fn format_specifier(
100 f: &mut Formatter<'_>,
101 date: Option<Date>,
102 time: Option<Time>,
103 offset: Option<UtcOffset>,
104 specifier: Specifier,
105 ) -> fmt::Result {
106 /// Push the provided specifier to the list of items.
107 macro_rules! specifier {
108 ($type:ident :: $specifier_fn:ident ( $specifier:ident $(, $param:expr)? )) => {
109 $type::$specifier_fn(
110 f,
111 $type.expect(concat!(
112 "Specifier `%",
113 stringify!($specifier),
114 "` requires a ",
115 stringify!($type),
116 " to be present."
117 )),
118 $($param)?
119 )?
120 };
121 }
122
123 macro_rules! literal {
124 ($string:literal) => {
125 f.write_str($string)?
126 };
127 }
128
129 use Specifier::*;
130 match specifier {
131 a => specifier!(date::fmt_a(a)),
132 A => specifier!(date::fmt_A(A)),
133 b => specifier!(date::fmt_b(b)),
134 B => specifier!(date::fmt_B(B)),
135 c => {
136 specifier!(date::fmt_a(a));
137 literal!(" ");
138 specifier!(date::fmt_b(b));
139 literal!(" ");
140 specifier!(date::fmt_d(d, Padding::None));
141 literal!(" ");
142 specifier!(time::fmt_H(H, Padding::None));
143 literal!(":");
144 specifier!(time::fmt_M(M, Padding::Zero));
145 literal!(":");
146 specifier!(time::fmt_S(S, Padding::Zero));
147 literal!(" ");
148 specifier!(date::fmt_Y(Y, Padding::None));
149 }
150 C { padding } => specifier!(date::fmt_C(C, padding)),
151 d { padding } => specifier!(date::fmt_d(d, padding)),
152 D => {
153 specifier!(date::fmt_m(m, Padding::None));
154 literal!("/");
155 specifier!(date::fmt_d(d, Padding::Zero));
156 literal!("/");
157 specifier!(date::fmt_y(y, Padding::Zero));
158 }
159 F => {
160 specifier!(date::fmt_Y(Y, Padding::None));
161 literal!("-");
162 specifier!(date::fmt_m(m, Padding::Zero));
163 literal!("-");
164 specifier!(date::fmt_d(d, Padding::Zero));
165 }
166 g { padding } => specifier!(date::fmt_g(g, padding)),
167 G { padding } => specifier!(date::fmt_G(G, padding)),
168 H { padding } => specifier!(time::fmt_H(H, padding)),
169 I { padding } => specifier!(time::fmt_I(I, padding)),
170 j { padding } => specifier!(date::fmt_j(j, padding)),
171 m { padding } => specifier!(date::fmt_m(m, padding)),
172 M { padding } => specifier!(time::fmt_M(M, padding)),
173 N => specifier!(time::fmt_N(N)),
174 p => specifier!(time::fmt_p(p)),
175 P => specifier!(time::fmt_P(P)),
176 r => {
177 specifier!(time::fmt_I(I, Padding::None));
178 literal!(":");
179 specifier!(time::fmt_M(M, Padding::Zero));
180 literal!(":");
181 specifier!(time::fmt_S(S, Padding::Zero));
182 literal!(" ");
183 specifier!(time::fmt_p(p));
184 }
185 R => {
186 specifier!(time::fmt_H(H, Padding::None));
187 literal!(":");
188 specifier!(time::fmt_M(M, Padding::Zero));
189 }
190 S { padding } => specifier!(time::fmt_S(S, padding)),
191 T => {
192 specifier!(time::fmt_H(H, Padding::None));
193 literal!(":");
194 specifier!(time::fmt_M(M, Padding::Zero));
195 literal!(":");
196 specifier!(time::fmt_S(S, Padding::Zero));
197 }
198 u => specifier!(date::fmt_u(u)),
199 U { padding } => specifier!(date::fmt_U(U, padding)),
200 V { padding } => specifier!(date::fmt_V(V, padding)),
201 w => specifier!(date::fmt_w(w)),
202 W { padding } => specifier!(date::fmt_W(W, padding)),
203 y { padding } => specifier!(date::fmt_y(y, padding)),
204 Y { padding } => specifier!(date::fmt_Y(Y, padding)),
205 z => specifier!(offset::fmt_z(z)),
206 }
207
208 Ok(())
209 }
210
211 /// An enum that can store both literals and specifiers.
212 #[allow(variant_size_differences, single_use_lifetimes)]
213 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
214 pub(crate) enum FormatItem<'a> {
215 /// A value that should be printed as-is.
216 Literal(&'a str),
217 /// A value that needs to be interpreted when formatting.
218 Specifier(Specifier),
219 }
220