interpolator/
macros.rs

1#[cfg(doc)]
2use crate::{format, write};
3
4/// [`format()`] as a macro to allow specifying the context
5/// directly.
6///
7/// For details on the context syntax see [`context!`](crate::context).
8///
9/// ```
10/// use interpolator::iformat;
11///
12/// assert_eq!(
13///     iformat!("{a}, {b:?}", a = 10, b:? = "test").unwrap(),
14///     "10, \"test\""
15/// )
16/// ```
17#[macro_export]
18macro_rules! iformat {
19    ($fmt:expr, $($context:tt)*) => {
20        $crate::format($fmt, &$crate::context!($($context)*))
21    };
22}
23
24/// [`write()`] as a macro to allow specifying the context
25/// directly.
26///
27/// For details on the context syntax see [`context!`](crate::context).
28///
29/// ```
30/// use std::fmt::Write;
31/// use interpolator::iwrite;
32///
33/// let mut buf = String::new();
34/// iwrite!(&mut buf, "{a}, {b:?}", a = 10, b:? = "test").unwrap();
35///
36/// assert_eq!( buf, "10, \"test\"" );
37/// ```
38#[macro_export]
39macro_rules! iwrite {
40    ($buf: expr, $fmt:expr, $($context:tt)*) => {
41        $crate::write($buf, $fmt, &$crate::context!($($context)*))
42    };
43}
44
45/// Like [`writeln!`] but using [`write()`].
46///
47/// For details on the context syntax see [`context!`](crate::context).
48///
49/// ```
50/// use std::fmt::Write;
51/// use interpolator::iwriteln;
52///
53/// let mut buf = String::new();
54/// iwriteln!(&mut buf, "{a}, {b:?}", a = 10, b:? = "test").unwrap();
55///
56/// assert_eq!( buf, "10, \"test\"\n" );
57/// ```
58#[macro_export]
59macro_rules! iwriteln {
60    ($buf: expr, $($fmt:tt)*) => {
61        match $crate::iwrite!($buf, $($fmt)*) {
62            // Cannot think of a better error index
63            Ok(()) => std::write!($buf, "\n").map_err(|e| $crate::Error::Fmt(e, 0)),
64            e => e
65        }
66    };
67}
68
69/// [`print!`] but using [`iformat!`].
70///
71/// Currently this allocates the complete formatted string.
72///
73/// For details on the context syntax see [`context!`](crate::context).
74///
75/// ```
76/// use interpolator::iprint;
77/// iprint!("{a}, {b:?}", a = 10, b:? = "test").unwrap();
78/// ```
79#[macro_export]
80macro_rules! iprint {
81    ($($fmt:tt)*) => {
82        $crate::iformat!($($fmt)*).map(|f| std::print!("{f}"))
83    };
84}
85
86/// [`println!`] but using [`iformat!`].
87///
88/// Currently this allocates the complete formatted string.
89///
90/// For details on the context syntax see [`context!`](crate::context).
91///
92/// ```
93/// use interpolator::iprintln;
94/// iprintln!("{a}, {b:?}", a = 10, b:? = "test").unwrap();
95/// ```
96#[macro_export]
97macro_rules! iprintln {
98    ($($fmt:tt)*) => {
99        $crate::iformat!($($fmt)*).map(|f| std::println!("{f}"))
100    };
101}
102
103/// [`eprint!`] but using [`iformat!`].
104///
105/// Currently this allocates the complete formatted string.
106///
107/// For details on the context syntax see [`context!`](crate::context).
108///
109/// ```
110/// use interpolator::ieprint;
111/// ieprint!("{a}, {b:?}", a = 10, b:? = "test").unwrap();
112/// ```
113#[macro_export]
114macro_rules! ieprint {
115    ($($fmt:tt)*) => {
116        $crate::iformat!($($fmt)*).map(|f| std::eprint!("{f}"))
117    };
118}
119
120/// [`eprintln!`] but using [`iformat!`].
121///
122/// Currently this allocates the complete formatted string.
123///
124/// For details on the context syntax see [`context!`](crate::context).
125///
126/// ```
127/// use interpolator::ieprintln;
128/// ieprintln!("{a}, {b:?}", a = 10, b:? = "test").unwrap();
129/// ```
130#[macro_export]
131macro_rules! ieprintln {
132    ($($fmt:tt)*) => {
133        $crate::iformat!($($fmt)*).map(|f| std::eprintln!("{f}"))
134    };
135}
136
137/// Creates a context for use with this crate's [`format()`] and [`write()`]
138/// functions.
139///
140/// It takes a comma seperated list of idents, followed by optional format
141/// specifiers and an optional value.
142///
143/// - `a` => key = `"a"`, value = `a`, traits = `[Display]`
144/// - `a:?` => key = `"a"`, value = `a`, traits = `[Debug]`
145/// - `a: :?` => key = `"a"`, value = `a`, traits = `[Debug, Display]`
146/// - `a = 5` => key = `"a"`, value = `5`, traits = `[Display]`
147/// - `a:?:x = 5` => key = `"a"`, value = `5`, traits = `[Debug, LowerHex]`
148///
149/// ```
150/// use interpolator::context;
151///
152/// let a = "hello";
153/// let context = context! {
154///     a,
155///     a:?, // This does not override the display implementation, only debug
156///     a: :?, // This overrides both display and debug
157///     a = 5, // Only overrides display
158///     a:?:x = 5, // Sets debug and lower hex implementation
159/// };
160/// ```
161///
162/// Due to borrow rules you might need to create let bindings for the values,
163/// i.e.
164///
165/// ```
166/// # use interpolator::context;
167/// let test = String::from("test");
168/// let context = context!(test);
169/// ```
170///
171/// instead of
172///
173/// ```compile_fail
174/// # use interpolator::context;
175/// let context = context!(test = String::from("test"));
176/// ```
177#[macro_export]
178macro_rules! context {
179    ($($tts:tt)*) => {
180        {
181            #[allow(unused)]
182            let mut map = ::std::collections::HashMap::<&str, $crate::Formattable>::new();
183            $crate::inner_context!(map; $($tts)*);
184            map
185        }
186    };
187}
188
189/// Creates a list of formattable values, applying th specified traits, or
190/// display if without a trait specifier.
191/// ```
192/// # use assert_dbg::assert_dbg;
193/// use interpolator::{iformat, list};
194/// // a list implementing Display
195/// let list = list![: ;"test", 10, "another"];
196/// # assert_dbg!(list, "[Formattable(display), Formattable(display), Formattable(display)]");
197/// let list = list![;"test", 10, "another"];
198/// # assert_dbg!(list, "[Formattable(display), Formattable(display), Formattable(display)]");
199/// let list = list!["test", 10, "another"];
200/// assert_eq!(
201///     iformat!("{list:i({})(, )}", list:i).unwrap(),
202///     "test, 10, another"
203/// );
204/// // a list implementing Display, Debug and UpperHex
205/// let list = list![: :?:X; 4, 0x10, 0xa4];
206/// assert_eq!(
207///     iformat!("{list:i({} {:?} {:X})(, )}", list:i).unwrap(),
208///     "4 4 4, 16 16 10, 164 164 A4"
209/// );
210/// ```
211///
212/// Due to borrow rules you might need to create let bindings for the items,
213/// i.e.
214///
215/// ```
216/// # use interpolator::list;
217/// let test = String::from("test");
218/// let list = list![test];
219/// ```
220///
221/// instead of
222///
223/// ```compile_fail
224/// # use interpolator::list;
225/// let list = list![String::from("test")];
226/// ```
227#[macro_export]
228#[cfg(doc)]
229macro_rules! list {
230    ($($(: $($trait:tt)?)*;)?($values:expr), *) => {};
231}
232
233#[cfg(not(doc))]
234#[allow(missing_docs)]
235#[macro_export]
236macro_rules! list {
237    ($($traits:ident),* : ; $($values:tt)*) => {
238        $crate::list!($($traits,)* display; $($values)*)
239    };
240    ($($traits:ident),* :: $($values:tt)*) => {
241        $crate::list!($($traits,)* display : $($values)*)
242    };
243    ($($traits:ident),* : : $($values:tt)*) => {
244        $crate::list!($($traits,)* display : $($values)*)
245    };
246    ($($traits:ident),* : ? $($values:tt)*) => {
247        $crate::list!($($traits,)* debug $($values)*)
248    };
249    ($($traits:ident),* : $trait:ident $($values:tt)*) => {
250        $crate::list!($($traits,)* $trait $($values)*)
251    };
252    ($(;)? $($value:expr),* $(,)?) => {
253        $crate::list!(:; $($value),*)
254    };
255    ($($traits:ident),*; $($value:expr),*) => {
256        $crate::apply!([], ($($traits)*), $($value),*)
257    };
258}
259
260#[doc(hidden)]
261#[macro_export]
262macro_rules! apply {
263    ($previous:tt, ($($traits:tt)*),) => {
264        $previous
265    };
266    ([$($previous:tt),*], ($($traits:tt)*), $value:expr $(, $($values:expr),*)?) => {
267        $crate::apply!( [ $($previous,)* {
268            #[allow(unused)]
269            let mut value = $crate::Formattable::default();
270            $($crate::set_trait!(value, $traits, $value);)*
271            value
272        }], ($($traits)*), $($($values),*)?)
273    };
274}
275
276#[macro_export]
277#[doc(hidden)]
278macro_rules! inner_context {
279    ($map:ident; $ident:ident$(:)? $(, $($tts:tt)*)?) => {
280        $crate::inner_context!($map, $ident, display; , $($($tts)*)?);
281        $crate::inner_context!($map; $($($tts)*)?)
282    };
283    ($map:ident; $ident:ident$(:)? = $($tts:tt)*) => {
284        $crate::inner_context!($map, $ident, display; = $($tts)*);
285    };
286    ($map:ident; $ident:ident : $($tts:tt)*) => {
287        $crate::inner_context!($map, $ident; : $($tts)*)
288    };
289    ($map:ident; $ident:ident :: $($tts:tt)*) => {
290        $crate::inner_context!($map, $ident; :: $($tts)*)
291    };
292    ($map:ident, $ident:ident$(, $traits:tt)*; :: $($tts:tt)*) => {
293        $crate::inner_context!($map, $ident$(, $traits)*, display; : $($tts)*)
294    };
295    ($map:ident, $ident:ident$(, $traits:tt)*; : : $($tts:tt)*) => {
296        $crate::inner_context!($map, $ident$(, $traits)*, display; : $($tts)*)
297    };
298    ($map:ident, $ident:ident$(, $traits:tt)*; : = $($tts:tt)*) => {
299        $crate::inner_context!($map, $ident$(, $traits)*, display; => $($tts)*)
300    };
301    ($map:ident, $ident:ident$(, $traits:tt)*; : $(, $($tts:tt)*)?) => {
302        $crate::inner_context!($map, $ident$(, $traits)*, display; $(, $($tts)*)?)
303    };
304    ($map:ident, $ident:ident$(, $traits:tt)*; :? $($tts:tt)*) => {
305        $crate::inner_context!($map, $ident$(, $traits)*, ?; $($tts)*)
306    };
307    ($map:ident, $ident:ident$(, $traits:tt)*; : $trait:ident $($tts:tt)*) => {
308        $crate::inner_context!($map, $ident$(, $traits)*, $trait; $($tts)*)
309    };
310    ($map:ident, $ident:ident$(, $traits:tt)*; $(, $($tts:tt)*)?) => {
311        $crate::inner_context!($map, $ident$(, $traits)*; = $ident, $($($tts)*)?)
312    };
313    ($map:ident, $ident:ident$(, $traits:tt)*; = $value:expr $(, $($tts:tt)*)?) => {
314        $($crate::set_trait!($map.entry(stringify!($ident)).or_default(), $traits, $value);)*
315        $crate::inner_context!($map; $($($tts)*)?)
316    };
317    ($map:ident;) => {};
318}
319
320/// Sets the corresponding trait to the symbol on a formattable.
321#[doc(hidden)]
322#[macro_export]
323macro_rules! set_trait {
324    ($formattable:expr, ? , $value:expr) => {
325        $crate::set_trait!($formattable, set_debug, $value);
326    };
327    ($formattable:expr,debug, $value:expr) => {
328        $crate::set_trait!($formattable, set_debug, $value);
329    };
330    ($formattable:expr, , $value:expr) => {
331        $crate::set_trait!($formattable, set_display, $value);
332    };
333    ($formattable:expr,display, $value:expr) => {
334        $crate::set_trait!($formattable, set_display, $value);
335    };
336    ($formattable:expr,integer, $value:expr) => {
337        $crate::set_trait!($formattable, set_integer, $value);
338    };
339    ($formattable:expr,float, $value:expr) => {
340        $crate::set_trait!($formattable, set_float, $value);
341    };
342    ($formattable:expr,b, $value:expr) => {
343        $crate::set_trait!($formattable, set_binary, $value);
344    };
345    ($formattable:expr,binary, $value:expr) => {
346        $crate::set_trait!($formattable, set_binary, $value);
347    };
348    ($formattable:expr,e, $value:expr) => {
349        $crate::set_trait!($formattable, set_lower_exp, $value);
350    };
351    ($formattable:expr,lower_exp, $value:expr) => {
352        $crate::set_trait!($formattable, set_lower_exp, $value);
353    };
354    ($formattable:expr,x, $value:expr) => {
355        $crate::set_trait!($formattable, set_lower_hex, $value);
356    };
357    ($formattable:expr,lower_hex, $value:expr) => {
358        $crate::set_trait!($formattable, set_lower_hex, $value);
359    };
360    ($formattable:expr,o, $value:expr) => {
361        $crate::set_trait!($formattable, set_octal, $value);
362    };
363    ($formattable:expr,octal, $value:expr) => {
364        $crate::set_trait!($formattable, set_octal, $value);
365    };
366    ($formattable:expr,E, $value:expr) => {
367        $crate::set_trait!($formattable, set_upper_exp, $value);
368    };
369    ($formattable:expr,upper_exp, $value:expr) => {
370        $crate::set_trait!($formattable, set_upper_exp, $value);
371    };
372    ($formattable:expr,X, $value:expr) => {
373        $crate::set_trait!($formattable, set_upper_hex, $value);
374    };
375    ($formattable:expr,upper_hex, $value:expr) => {
376        $crate::set_trait!($formattable, set_upper_hex, $value);
377    };
378    ($formattable:expr,p, $value:expr) => {
379        $crate::set_trait!($formattable, set_pointer, $value);
380    };
381    ($formattable:expr,pointer, $value:expr) => {
382        $crate::set_trait!($formattable, set_pointer, $value);
383    };
384    ($formattable:expr,i, $value:expr) => {
385        $crate::set_trait!($formattable, set_iter, $value);
386    };
387    ($formattable:expr,iter, $value:expr) => {
388        $crate::set_trait!($formattable, set_iter, $value);
389    };
390    ($formattable:expr, $setter:ident, $value:expr) => {
391        $formattable.$setter(&$value);
392    };
393}
394
395#[cfg(test)]
396mod test {
397    #[allow(unused)]
398    use std::f32::consts::PI;
399
400    #[allow(unused)]
401    macro_rules! assert_fmt {
402        ($fmt:tt, $context:tt. $($fn:tt),+($key:tt), $expected:tt) => {
403            assert_eq!(
404                format!(
405                    $fmt,
406                    $($context.get(stringify!($key)).unwrap().$fn().unwrap()),+),
407                    $expected
408                );
409        };
410    }
411
412    #[test]
413    #[cfg(all(feature = "debug", feature = "number", feature = "pointer"))]
414    fn context() {
415        let pointer = &32;
416        let context = context! {
417            a = "",
418            a:? = "",
419            b:?:b = 10,
420            d: :? = "hello",
421        };
422        assert_fmt!("{}", context.get_display(a), "");
423        assert_fmt!("{:?}", context.get_debug(a), "\"\"");
424        assert_fmt!("{:?}", context.get_debug(b), "10");
425        assert_fmt!("{:b}", context.get_binary(b), "1010");
426        assert_fmt!("{:}", context.get_display(d), "hello");
427        assert_fmt!("{:?}", context.get_debug(d), "\"hello\"");
428        let context = context! {
429            debug1:? = "debug",
430            debug2:debug = "debug",
431            display1 = "display",
432            display2: = "display",
433            display3:display = "display",
434            integer:integer = 42,
435            float:float = PI,
436            binary1:b = 12,
437            binary2:binary = 12,
438            lower_exp1:e = 0.01,
439            lower_exp2:lower_exp = 0.01,
440            lower_hex1:x = 0xA9,
441            lower_hex2:lower_hex = 0xA9,
442            octal1:o = 0o20,
443            octal2:octal = 0o20,
444            upper_exp1:E = 0.01,
445            upper_exp2:upper_exp = 0.01,
446            upper_hex1:X = 0xA9,
447            upper_hex2:upper_hex = 0xA9,
448            pointer1:p = pointer,
449            pointer2:pointer = pointer,
450            // iter:i:? = [1, 2],
451        };
452        assert_fmt!("{}", context.get_display(display1), "display");
453        assert_fmt!("{}", context.get_display(display2), "display");
454        assert_fmt!("{}", context.get_display(display3), "display");
455        assert_fmt!("{:?}", context.get_debug(debug1), "\"debug\"");
456        assert_fmt!("{:?}", context.get_debug(debug2), "\"debug\"");
457        assert_fmt!(
458            "{:b} {:?} {:} {:e} {:x} {:o} {:E} {:X}",
459            context.get_binary,
460            get_debug,
461            get_display,
462            get_lower_exp,
463            get_lower_hex,
464            get_octal,
465            get_upper_exp,
466            get_upper_hex(integer),
467            "101010 42 42 4.2e1 2a 52 4.2E1 2A"
468        );
469        assert_fmt!(
470            "{:?} {:} {:e} {:E}",
471            context.get_debug,
472            get_display,
473            get_lower_exp,
474            get_upper_exp(float),
475            "3.1415927 3.1415927 3.1415927e0 3.1415927E0"
476        );
477        assert_fmt!("{:b}", context.get_binary(binary1), "1100");
478        assert_fmt!("{:b}", context.get_binary(binary2), "1100");
479        assert_fmt!("{:e}", context.get_lower_exp(lower_exp1), "1e-2");
480        assert_fmt!("{:e}", context.get_lower_exp(lower_exp2), "1e-2");
481        assert_fmt!("{:x}", context.get_lower_hex(lower_hex1), "a9");
482        assert_fmt!("{:x}", context.get_lower_hex(lower_hex2), "a9");
483        assert_fmt!("{:o}", context.get_octal(octal1), "20");
484        assert_fmt!("{:o}", context.get_octal(octal2), "20");
485        assert_fmt!("{:E}", context.get_upper_exp(upper_exp1), "1E-2");
486        assert_fmt!("{:E}", context.get_upper_exp(upper_exp2), "1E-2");
487        assert_fmt!("{:X}", context.get_upper_hex(upper_hex1), "A9");
488        assert_fmt!("{:X}", context.get_upper_hex(upper_hex2), "A9");
489        assert_fmt!(
490            "{:p}",
491            context.get_pointer(pointer1),
492            (format!("{pointer:p}"))
493        );
494        assert_fmt!(
495            "{:p}",
496            context.get_pointer(pointer2),
497            (format!("{pointer:p}"))
498        );
499    }
500    #[allow(unused)]
501    macro_rules! assert_i {
502        ($fmt:literal, $variable:ident, $rhs:literal) => {
503            assert_eq!(
504                iformat!(concat!("{list:i(", $fmt, ")(,)}"), $variable: i).unwrap(),
505                $rhs
506            );
507        };
508    }
509
510    #[test]
511    #[cfg(feature = "iter")]
512    fn list() {
513        let list = list!["a", 1];
514        assert_i!("{}", list, "a,1");
515        #[cfg(feature = "debug")]
516        {
517            let list = list![:?; "a", 1];
518            assert_i!("{:?}", list, "\"a\",1");
519            #[cfg(feature = "number")]
520            {
521                let list = list![::?:X; 2, 1];
522                assert_i!("{}{:?}{:X}", list, "222,111");
523            }
524        }
525    }
526}