formatx/
macros.rs

1/// Creates a `String` using interpolation of runtime expressions at runtime.
2///
3/// The first argument `formatx!` receives is a format string.
4/// The power of the formatting string is in the `{}`s contained.
5///
6/// Additional parameters passed to `formatx!` replace the `{}`s within the
7/// formatting string in the order given unless named or positional parameters
8/// are used; see [std::fmt](std::fmt) for more information.
9///
10/// A common use for `formatx!` is concatenation and interpolation of strings at runtime.
11/// The same convention is used with [print!](std::print) and [write!](core::write) macros,
12/// depending on the intended destination of the string.
13///
14/// To convert a single value to a string, use the `to_string` method. This
15/// will use the [Display](core::fmt::Display) formatting trait.
16///
17/// # Panics
18///
19/// `formatx!` panics if a formatting trait implementation returns an error.
20/// This indicates an incorrect implementation
21/// since `fmt::Write for String` never returns an error itself.
22/// Additonally `formatx!` returns a `Result<String, formatx::Error>` type which can be resolved later.
23///
24/// # Examples
25///
26/// Any type could be formatted if it implements [Display](std::fmt::Display) + [Debug](std::fmt::Debug) traits.
27///
28/// ```
29/// use formatx::formatx;
30///
31/// formatx!("test").unwrap();
32/// formatx!("hello {}", "world!").unwrap();
33/// formatx!("x = {}, y = {y}", 10, y = 30).unwrap();
34/// ```
35#[macro_export]
36macro_rules! formatx {
37    ($template: expr $(,)?) => {
38        || -> ::std::result::Result<String, $crate::Error> {
39            $crate::Template::new($template)?.text()
40        }()
41    };
42
43    ($template: expr, $($values: tt)*) => {
44        || -> ::std::result::Result<String, $crate::Error> {
45            let mut template = $crate::Template::new($template)?;
46            $crate::_formatx_internal!(template, $($values)*);
47            template.text()
48        }()
49    };
50}
51
52#[macro_export]
53#[doc(hidden)]
54macro_rules! _formatx_internal {
55    ($template: expr, $name: ident = $value: expr $(,)?) => {
56        $template.replace(stringify!($name), $value);
57    };
58
59    ($template: expr, $value: expr $(,)?) => (
60        $template.replace_positional($value);
61    );
62
63    ($template: expr, $name: ident = $value: expr, $($values: tt)*) => {
64        $template.replace(stringify!($name), $value);
65        $crate::_formatx_internal!($template, $($values)*);
66    };
67
68    ($template: expr, $value:expr, $($values: tt)*) => {
69    	$template.replace_positional($value);
70        $crate::_formatx_internal!($template, $($values)*);
71    };
72}
73
74#[cfg(test)]
75mod tests {
76    use crate::formatx;
77
78    macro_rules! formatx_test {
79        ($template: expr, $($values: tt)*) => {
80            assert_eq!(formatx!($template, $($values)*).unwrap(), format!($template, $($values)*))
81        }
82    }
83
84    #[test]
85    fn text() {
86        assert_eq!(formatx!("Hello").unwrap(), format!("Hello"));
87    }
88
89    #[test]
90    fn positional() {
91        formatx_test!("Hello, {}!", "world");
92        formatx_test!("The number is {}", 1);
93        formatx_test!("{} {}", 1, 2);
94    }
95
96    #[test]
97    fn named() {
98        formatx_test!("{value}", value = 4);
99        formatx_test!("{argument}", argument = "test");
100        formatx_test!("{a} {c} {b}", a = "a", b = 'b', c = 3);
101    }
102
103    #[test]
104    fn intermixed() {
105        formatx_test!("{name} {}", 1, name = 2);
106    }
107
108    #[test]
109    fn width() {
110        formatx_test!("Hello {:5}!", "x");
111        formatx_test!("Hello {:05}!", -5);
112    }
113
114    #[test]
115    fn zero_width() {
116        formatx_test!("{:04}", 42);
117        formatx_test!("Hello {:05}!", 5);
118    }
119
120    #[test]
121    fn align() {
122        formatx_test!("Hello {:<5}!", "x");
123        formatx_test!("Hello {:-<5}!", "x");
124        formatx_test!("Hello {:^5}!", "x");
125        formatx_test!("Hello {:>5}!", "x");
126    }
127
128    #[test]
129    fn sign() {
130        formatx_test!("Hello {:+}!", 5)
131    }
132
133    #[test]
134    fn precision() {
135        formatx_test!("Hello {0} is {1:.5}", "x", 0.01)
136    }
137
138    #[test]
139    fn escaping() {
140        assert_eq!(formatx!("Hello {{}}").unwrap(), format!("Hello {{}}"));
141        assert_eq!(formatx!("{{ Hello").unwrap(), format!("{{ Hello"));
142    }
143
144    #[test]
145    fn debug() {
146        formatx_test!("{} {:?}", 3, 4);
147        formatx_test!("{} {:?}", 'a', 'b');
148        formatx_test!("{} {:?}", "foo\n", "bar\n");
149    }
150
151    #[test]
152    fn trailing_commas() {
153        formatx_test!("Hello",);
154        formatx_test!("Hello {}", "world",);
155        formatx_test!("Hello {1} {0}", "1", "0",);
156        formatx_test!("Hello {name}", name = "John",);
157    }
158}