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)]
75#[allow(dead_code)]
76enum Error {
77 Format(#[from] self::formatter::FormatError),
78 Parse(Box<self::parser::Error>),
79}
80
81impl From<self::parser::Error> for Error {
82 fn from(err: self::parser::Error) -> Self {
83 Self::Parse(Box::new(err))
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use std::f64::consts::PI;
90
91 #[test]
92 fn test_sprintf() {
93 let res = sprintf!("Hello, %(name)s!", name = "world").unwrap();
94 assert_eq!(res, "Hello, world!");
95 assert_eq!("%", sprintf!("%%").unwrap());
96 assert_eq!("10", sprintf!("%b", 2).unwrap());
97 assert_eq!("A", sprintf!("%c", 65).unwrap());
98 assert_eq!("2", sprintf!("%d", 2).unwrap());
99 assert_eq!("2", sprintf!("%i", 2).unwrap());
100 assert_eq!(
103 r#"{"foo":"bar"}"#,
104 sprintf!("%j", serde_json::json!({"foo": "bar"})).unwrap()
105 );
106 assert_eq!(r#"["foo","bar"]"#, sprintf!("%j", ["foo", "bar"]).unwrap());
107 assert_eq!("2e0", sprintf!("%e", 2).unwrap()); assert_eq!("2", sprintf!("%u", 2).unwrap());
109 assert_eq!("4294967294", sprintf!("%u", -2).unwrap());
110 assert_eq!("2.2", sprintf!("%f", 2.2).unwrap());
111 assert_eq!("3.141592653589793", sprintf!("%g", PI).unwrap());
112 assert_eq!("10", sprintf!("%o", 8).unwrap());
113 assert_eq!("37777777770", sprintf!("%o", -8).unwrap());
114 assert_eq!("%s", sprintf!("%s", "%s").unwrap());
115 assert_eq!("ff", sprintf!("%x", 255).unwrap());
116 assert_eq!("ffffff01", sprintf!("%x", -255).unwrap());
117 assert_eq!("FF", sprintf!("%X", 255).unwrap());
118 assert_eq!("FFFFFF01", sprintf!("%X", -255).unwrap());
119 assert_eq!(
120 "Polly wants a cracker",
121 sprintf!("%2$s %3$s a %1$s", "cracker", "Polly", "wants").unwrap()
122 );
123 assert_eq!(
124 "Hello world!",
125 sprintf!("Hello %(who)s!", who = "world").unwrap()
126 );
127
128 assert_eq!("true", sprintf!("%t", true).unwrap());
129 assert_eq!("t", sprintf!("%.1t", true).unwrap());
130 assert_eq!("false", sprintf!("%t", false).unwrap());
134 assert_eq!("f", sprintf!("%.1t", false).unwrap());
135 assert_eq!("null", sprintf!("%T", serde_json::json!(null)).unwrap());
139 assert_eq!("boolean", sprintf!("%T", true).unwrap());
140 assert_eq!("number", sprintf!("%T", 42).unwrap());
141 assert_eq!("string", sprintf!("%T", "This is a string").unwrap());
142 assert_eq!("array", sprintf!("%T", [1, 2, 3]).unwrap());
143 assert_eq!(
144 "object",
145 sprintf!("%T", serde_json::json!({"foo": "bar"})).unwrap()
146 );
147 }
148
149 #[test]
150 fn test_complex() {
151 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!("%+d", -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", sprintf!("%+i", -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.2", sprintf!("%+f", -2.2).unwrap());
164 assert_eq!("-2.3", sprintf!("%+.1f", -2.34).unwrap());
165 assert_eq!("-0.0", sprintf!("%+.1f", -0.01).unwrap());
166
167 assert_eq!("3.14159", sprintf!("%.6g", PI).unwrap());
168 assert_eq!("3.14", sprintf!("%.3g", PI).unwrap());
169 assert_eq!("3", sprintf!("%.1g", PI).unwrap());
170 assert_eq!("3e5", sprintf!("%.1g", 300_000.0).unwrap());
171 assert_eq!("300", sprintf!("%.3g", 300).unwrap());
172
173 assert_eq!("-000000123", sprintf!("%+010d", -123).unwrap());
174 assert_eq!("______-123", sprintf!("%+'_10d", -123).unwrap());
175 assert_eq!("-234.34 123.2", sprintf!("%f %f", -234.34, 123.2).unwrap());
176
177 assert_eq!("-0002", sprintf!("%05d", -2).unwrap());
179 assert_eq!("-0002", sprintf!("%05i", -2).unwrap());
180 assert_eq!(" <", sprintf!("%5s", "<").unwrap());
181 assert_eq!("0000<", sprintf!("%05s", "<").unwrap());
182 assert_eq!("____<", sprintf!("%'_5s", "<").unwrap());
183 assert_eq!("> ", sprintf!("%-5s", ">").unwrap());
184 assert_eq!(">0000", sprintf!("%0-5s", ">").unwrap());
185 assert_eq!(">____", sprintf!("%'_-5s", ">").unwrap());
186 assert_eq!("xxxxxx", sprintf!("%5s", "xxxxxx").unwrap());
187 assert_eq!("1234", sprintf!("%02u", 1234).unwrap());
188 assert_eq!(" -10.235", sprintf!("%8.3f", -10.23456).unwrap());
189 assert_eq!("-12.34 xxx", sprintf!("%f %s", -12.34, "xxx").unwrap());
190 assert_eq!(
191 r#"{
192 "foo": "bar"
193}"#,
194 sprintf!("%2j", serde_json::json!({"foo": "bar"})).unwrap()
195 );
196 assert_eq!(
197 r#"[
198 "foo",
199 "bar"
200]"#,
201 sprintf!("%2j", ["foo", "bar"]).unwrap()
202 );
203
204 assert_eq!("2.3", sprintf!("%.1f", 2.345).unwrap());
206 assert_eq!("xxxxx", sprintf!("%5.5s", "xxxxxx").unwrap());
207 assert_eq!(" x", sprintf!("%5.1s", "xxxxxx").unwrap());
208 }
209}