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