1#![allow(unused_macros)]
8
9mod argument;
10mod formatter;
11mod message;
12mod parser;
13
14pub use self::{
15 argument::{Argument, List as ArgumentList},
16 formatter::{FormatError, FormattedMessage, FormattedMessagePart},
17 message::Message,
18};
19
20macro_rules! arg_list_inner {
21 ($var:ident |) => { };
22 ($var:ident | $name:ident = $($arg:expr)*, $($rest:tt)*) => {{
23 $var.push($crate::sprintf::Argument::from((stringify!($name), ::serde_json::json!($($arg)*))));
24 $crate::sprintf::arg_list_inner!($var | $($rest)* );
25 }};
26 ($var:ident | $name:ident = $($arg:expr)*) => {{
27 $var.push($crate::sprintf::Argument::from((stringify!($name), ::serde_json::json!($($arg)*))));
28 }};
29 ($var:ident | $($arg:expr)*, $($rest:tt)*) => {{
30 $var.push($crate::sprintf::Argument::from(::serde_json::json!($($arg)*)));
31 $crate::sprintf::arg_list_inner!($var | $($rest)* );
32 }};
33 ($var:ident | $($arg:expr)*) => {{
34 $var.push($crate::sprintf::Argument::from(::serde_json::json!($($arg)*)));
35 }};
36}
37
38macro_rules! arg_list {
39 ($($args:tt)*) => {{
40 let mut __args = Vec::<$crate::sprintf::Argument>::new();
41 $crate::sprintf::arg_list_inner!(__args | $($args)* );
42 $crate::sprintf::ArgumentList::from_iter(__args)
43 }}
44}
45
46macro_rules! sprintf {
47 ($message:literal) => {{
48 <$crate::sprintf::Message as ::std::str::FromStr>::from_str($message)
49 .map_err($crate::sprintf::Error::from)
50 .and_then(|message| {
51 let __args = $crate::sprintf::ArgumentList::default();
52 message.format(&__args).map_err($crate::sprintf::Error::from)
53 })
54 }};
55
56 ($message:literal, $($args:tt)*) => {{
57 <$crate::sprintf::Message as ::std::str::FromStr>::from_str($message)
58 .map_err($crate::sprintf::Error::from)
59 .and_then(|message| {
60 let __args = $crate::sprintf::arg_list!($($args)*);
61 message.format(&__args).map_err($crate::sprintf::Error::from)
62 })
63 }};
64}
65
66#[allow(unused_imports)]
67pub(crate) use arg_list;
68#[allow(unused_imports)]
69pub(crate) use arg_list_inner;
70#[allow(unused_imports)]
71pub(crate) use sprintf;
72
73#[derive(Debug, thiserror::Error)]
74#[error(transparent)]
75enum Error {
76 Format(#[from] self::formatter::FormatError),
77 Parse(Box<self::parser::Error>),
78}
79
80impl From<self::parser::Error> for Error {
81 fn from(err: self::parser::Error) -> Self {
82 Self::Parse(Box::new(err))
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use std::f64::consts::PI;
89
90 #[test]
91 fn test_sprintf() {
92 let res = sprintf!("Hello, %(name)s!", name = "world").unwrap();
93 assert_eq!(res, "Hello, world!");
94 assert_eq!("%", sprintf!("%%").unwrap());
95 assert_eq!("10", sprintf!("%b", 2).unwrap());
96 assert_eq!("A", sprintf!("%c", 65).unwrap());
97 assert_eq!("2", sprintf!("%d", 2).unwrap());
98 assert_eq!("2", sprintf!("%i", 2).unwrap());
99 assert_eq!(
102 r#"{"foo":"bar"}"#,
103 sprintf!("%j", serde_json::json!({"foo": "bar"})).unwrap()
104 );
105 assert_eq!(r#"["foo","bar"]"#, sprintf!("%j", ["foo", "bar"]).unwrap());
106 assert_eq!("2e0", sprintf!("%e", 2).unwrap()); assert_eq!("2", sprintf!("%u", 2).unwrap());
108 assert_eq!("4294967294", sprintf!("%u", -2).unwrap());
109 assert_eq!("2.2", sprintf!("%f", 2.2).unwrap());
110 assert_eq!("3.141592653589793", sprintf!("%g", PI).unwrap());
111 assert_eq!("10", sprintf!("%o", 8).unwrap());
112 assert_eq!("37777777770", sprintf!("%o", -8).unwrap());
113 assert_eq!("%s", sprintf!("%s", "%s").unwrap());
114 assert_eq!("ff", sprintf!("%x", 255).unwrap());
115 assert_eq!("ffffff01", sprintf!("%x", -255).unwrap());
116 assert_eq!("FF", sprintf!("%X", 255).unwrap());
117 assert_eq!("FFFFFF01", sprintf!("%X", -255).unwrap());
118 assert_eq!(
119 "Polly wants a cracker",
120 sprintf!("%2$s %3$s a %1$s", "cracker", "Polly", "wants").unwrap()
121 );
122 assert_eq!(
123 "Hello world!",
124 sprintf!("Hello %(who)s!", who = "world").unwrap()
125 );
126
127 assert_eq!("true", sprintf!("%t", true).unwrap());
128 assert_eq!("t", sprintf!("%.1t", true).unwrap());
129 assert_eq!("false", sprintf!("%t", false).unwrap());
133 assert_eq!("f", sprintf!("%.1t", false).unwrap());
134 assert_eq!("null", sprintf!("%T", serde_json::json!(null)).unwrap());
138 assert_eq!("boolean", sprintf!("%T", true).unwrap());
139 assert_eq!("number", sprintf!("%T", 42).unwrap());
140 assert_eq!("string", sprintf!("%T", "This is a string").unwrap());
141 assert_eq!("array", sprintf!("%T", [1, 2, 3]).unwrap());
142 assert_eq!(
143 "object",
144 sprintf!("%T", serde_json::json!({"foo": "bar"})).unwrap()
145 );
146 }
147
148 #[test]
149 fn test_complex() {
150 assert_eq!("2", sprintf!("%d", 2).unwrap());
152 assert_eq!("-2", sprintf!("%d", -2).unwrap());
153 assert_eq!("+2", sprintf!("%+d", 2).unwrap());
154 assert_eq!("-2", sprintf!("%+d", -2).unwrap());
155 assert_eq!("2", sprintf!("%i", 2).unwrap());
156 assert_eq!("-2", sprintf!("%i", -2).unwrap());
157 assert_eq!("+2", sprintf!("%+i", 2).unwrap());
158 assert_eq!("-2", sprintf!("%+i", -2).unwrap());
159 assert_eq!("2.2", sprintf!("%f", 2.2).unwrap());
160 assert_eq!("-2.2", sprintf!("%f", -2.2).unwrap());
161 assert_eq!("+2.2", sprintf!("%+f", 2.2).unwrap());
162 assert_eq!("-2.2", sprintf!("%+f", -2.2).unwrap());
163 assert_eq!("-2.3", sprintf!("%+.1f", -2.34).unwrap());
164 assert_eq!("-0.0", sprintf!("%+.1f", -0.01).unwrap());
165
166 assert_eq!("3.14159", sprintf!("%.6g", PI).unwrap());
167 assert_eq!("3.14", sprintf!("%.3g", PI).unwrap());
168 assert_eq!("3", sprintf!("%.1g", PI).unwrap());
169 assert_eq!("3e5", sprintf!("%.1g", 300_000.0).unwrap());
170 assert_eq!("300", sprintf!("%.3g", 300).unwrap());
171
172 assert_eq!("-000000123", sprintf!("%+010d", -123).unwrap());
173 assert_eq!("______-123", sprintf!("%+'_10d", -123).unwrap());
174 assert_eq!("-234.34 123.2", sprintf!("%f %f", -234.34, 123.2).unwrap());
175
176 assert_eq!("-0002", sprintf!("%05d", -2).unwrap());
178 assert_eq!("-0002", sprintf!("%05i", -2).unwrap());
179 assert_eq!(" <", sprintf!("%5s", "<").unwrap());
180 assert_eq!("0000<", sprintf!("%05s", "<").unwrap());
181 assert_eq!("____<", sprintf!("%'_5s", "<").unwrap());
182 assert_eq!("> ", sprintf!("%-5s", ">").unwrap());
183 assert_eq!(">0000", sprintf!("%0-5s", ">").unwrap());
184 assert_eq!(">____", sprintf!("%'_-5s", ">").unwrap());
185 assert_eq!("xxxxxx", sprintf!("%5s", "xxxxxx").unwrap());
186 assert_eq!("1234", sprintf!("%02u", 1234).unwrap());
187 assert_eq!(" -10.235", sprintf!("%8.3f", -10.23456).unwrap());
188 assert_eq!("-12.34 xxx", sprintf!("%f %s", -12.34, "xxx").unwrap());
189 assert_eq!(
190 r#"{
191 "foo": "bar"
192}"#,
193 sprintf!("%2j", serde_json::json!({"foo": "bar"})).unwrap()
194 );
195 assert_eq!(
196 r#"[
197 "foo",
198 "bar"
199]"#,
200 sprintf!("%2j", ["foo", "bar"]).unwrap()
201 );
202
203 assert_eq!("2.3", sprintf!("%.1f", 2.345).unwrap());
205 assert_eq!("xxxxx", sprintf!("%5.5s", "xxxxxx").unwrap());
206 assert_eq!(" x", sprintf!("%5.1s", "xxxxxx").unwrap());
207 }
208}