fomat_macros/lib.rs
1//! This crate provides alternative syntax for
2//! `write!`, `writeln!`, `print!`, `println!` and `format!` macros.
3//! It also introduces macros to print on `stderr`.
4//!
5//! The names of macros in this crate are formed by
6//! removing the letter `r` from their `std` counterparts.
7//!
8//! Index: [**examples**](#examples) •
9//! [**syntax**](#syntax-overview):
10//! [`"string"`](#string-literals),
11//! [`()`, `[]`](#expressions-in--and--brackets),
12//! [`{}`](#curly-braces),
13//! [`for`](#for-loops),
14//! [`if`](#if-and-if-let),
15//! [`match`](#match),
16//! [`=`](#debugging-shorthand) •
17//! [**troubleshooting**](#troubleshooting) •
18//! [**macros**](#macros)
19//!
20//! # Examples
21//!
22//! ```
23//! use fomat_macros::pintln;
24//!
25//! fn main() {
26//! pintln!("Hello, World!");
27//! pintln!("Display trait: "(2+2));
28//! pintln!("Debug trait: "[vec![1, 2, 3]]);
29//! pintln!("Multiple " "parameters" (1) " " [2]);
30//!
31//! pintln!("Formatting parameters: " {(1./3.):5.2}); // 0.333
32//! pintln!("Debug: "[= 2 + 2]); // Debug: 2 + 2 = 4
33//! }
34//! ```
35//!
36//! This crate also contains a small templating language,
37//! allowing you to mix constructs like `for` with
38//! the printing syntax. The following should print `1 :: 2 :: 3 :: nil`.
39//!
40//! ```
41//! # use fomat_macros::pintln;
42//! # fn main() {
43//! let list = [1, 2, 3];
44//! pintln!( for x in &list { (x) " :: " } "nil" );
45//! # }
46//! ```
47//!
48//! You can also use the macros without importing them
49//! ```
50//! fomat_macros::pintln!("2 + 2 = "(2 + 2));
51//! ```
52//!
53//! # Syntax overview
54//!
55//! All the macros share the same syntax, so
56//! it will be described in this section.
57//!
58//! The macros take list of *things* to print as an argument.
59//! Each *thing* could be either a string literal, something
60//! inside brackets (`()`, `[]` or `{}`) or a Rust construct
61//! (`for`, `if let`, `if` or `match`). There has to be
62//! no separator (like a comma) between those *things*.
63//!
64//! Whitespace is ignored outside the string literals.
65//!
66//! ## String literals
67//!
68//! String literals will be formatted directly as they are.
69//! Note that this also applies to `{` and `}` characters.
70//!
71//! ```
72//! # use fomat_macros::fomat;
73//! # fn main() {
74//! let s = fomat!("Hi." "{}");
75//! assert_eq!(s, "Hi.{}");
76//! # }
77//! ```
78//!
79//! ## Expressions in `()` and `[]` brackets.
80//!
81//! Expressions in these brackets will be evaluated and
82//! printed using:
83//!
84//! * `Display` trait for `(expr)` (equivalent to `{}` format).
85//! * `Debug` trait for `[expr]` (equivalent to `{:?}` format).
86//!
87//! Like in `std`, they are implicitly borrowed.
88//!
89//! ```
90//! # use fomat_macros::fomat;
91//! # fn main() {
92//! let s = fomat!( ("string") (2 + 2) ", " [vec![1]] );
93//! assert_eq!(s, "string4, [1]")
94//! # }
95//! ```
96//!
97//! ## Curly braces
98//!
99//! ### `write!` passthrough
100//!
101//! If you want to use regular `format!` syntax for some
102//! part of your string, place `format!` arguments
103//! inside the curly braces:
104//!
105//! ```
106//! # use fomat_macros::wite;
107//! # fn main() {
108//! use std::io::Write;
109//!
110//! let mut v = vec![];
111//! wite!(v, "foo " {"{} baz {}", "bar", "quux"});
112//! assert_eq!(v, "foo bar baz quux".as_bytes());
113//! # }
114//! ```
115//!
116//! ### Single argument
117//!
118//! If you only want to print a single argument
119//! with a custom format parameters,
120//! you can use the `{token_tree:format_parameters}`
121//! syntax.
122//!
123//! The following will use binary format,
124//! zero-aligned to 8 places.
125//!
126//! ```
127//! # use fomat_macros::fomat;
128//! # fn main() {
129//! let s = fomat!({13:08b});
130//! assert_eq!(s, "00001101");
131//! # }
132//! ```
133//!
134//! Please note that there can be only a single
135//! token tree before the colon – usually
136//! a literal or an identifier. Anything
137//! longer has to be wrapped in parentheses
138//! (like that `{(10+3):08b}`).
139//!
140//! ## For loops
141//!
142//! For loops use the regular Rust syntax,
143//! except the body
144//! will use this printing syntax again.
145//!
146//! ```
147//! # use fomat_macros::fomat;
148//! # fn main() {
149//! let list = [1, 2, 3];
150//! let s = fomat!( for x in &list { (x) " :: " } "nil" );
151//! assert_eq!(s, "1 :: 2 :: 3 :: nil");
152//! # }
153//! ```
154//!
155//! For loops can also use an optional separator,
156//! denoted by `sep` or `separated` keyword.
157//!
158//! ```
159//! # use fomat_macros::fomat;
160//! # fn main() {
161//! # let list = ["a", "b"];
162//! let s = fomat!(
163//! for (i, x) in list.iter().enumerate() { (i) " → " (x) }
164//! separated { ", " }
165//! );
166//! assert_eq!(s, "0 → a, 1 → b");
167//! # }
168//! ```
169//!
170//! For loops (and other syntax elements) can also be nested:
171//!
172//! ```
173//! # use fomat_macros::fomat;
174//! # fn main() {
175//! let matrix = [[0, 1], [2, 3]];
176//! assert_eq!(
177//! fomat!( for row in &matrix { for x in row { {x:3} } "\n" } ),
178//! " 0 1\n 2 3\n"
179//! );
180//! # }
181//! ```
182//!
183//! ## If and if let
184//!
185//! They use the regular Rust syntax,
186//! except of the body (inside `{}`),
187//! which uses the printing syntax.
188//!
189//! The benefits of using this syntax instead
190//! of getting `if` "outside" of the printing
191//! macros is apparent when the conditional is
192//! a part of a longer string (you don't
193//! have to split this into three separate `write!`s):
194//!
195//! ```
196//! # use fomat_macros::fomat;
197//! # fn main() {
198//! let opt = Some(5);
199//! let s = fomat!(
200//! "a\n"
201//! if let Some(x) = opt { (x) "\n" } else { "nothing\n" }
202//! "b\n"
203//! );
204//! assert_eq!(s, "a\n5\nb\n");
205//! # }
206//! ```
207//!
208//! The `else` clause is optional.
209//!
210//! `else if`-chaining is not supported. As a workaround,
211//! use `else { if ... }` or `match`.
212//!
213//! ## Match
214//!
215//! Match uses the regular Rust syntax,
216//! except arms has to use `{}` blocks,
217//! which will be interpreted using printing syntax.
218//!
219//! ```
220//! # use fomat_macros::fomat;
221//! # fn main() {
222//! let v = [Some(1), None, Some(2)];
223//! let s = fomat!(
224//! for x in &v {
225//! match *x {
226//! Some(x) => { (x) }
227//! None => { "_" }
228//! }
229//! }
230//! );
231//! assert_eq!(s, "1_2");
232//! # }
233//! ```
234//!
235//! Match arms should not be separated by commas.
236//!
237//! ## Debugging shorthand
238//!
239//! If you want to print both the expression and the value,
240//! place equal sign as a first character in brackets.
241//! The trait used to print the value will depend on
242//! the kind of brackets used.
243//!
244//! ```
245//! # use fomat_macros::fomat;
246//! # fn main() {
247//! let word = "foo";
248//! let arr = [10];
249//! let s = fomat!( (=word) ", " [=&arr] ", " {=5:#b} );
250//! assert_eq!(s, "word = foo, &arr = [10], 5 = 0b101");
251//! # }
252//! ```
253//!
254//! # Troubleshooting
255//!
256//! ## Recursion depth
257//!
258//! If you hit the error about recursion depth,
259//! which occurs when you try to print more than
260//! about 50 elements, you can use this workaround
261//! instead of increasing the limit: split everything
262//! into two (or more) dummy `if true` blocks.
263//!
264//! ## Errors in macro parsing
265//!
266//! If you hit `expected a literal`, that either means
267//! either you've made a syntactic mistake
268//! or really a string literal is expected here.
269//! Remember, naked identifiers won't be printed
270//! unless you put them in parentheses.
271
272use std::fmt;
273
274#[doc(hidden)]
275pub struct DisplayOnce<F> {
276 closure: std::cell::Cell<Option<F>>
277}
278
279impl<F> DisplayOnce<F>
280where
281 F: FnOnce(&mut fmt::Formatter) -> fmt::Result
282{
283 pub fn new(f: F) -> Self {
284 Self { closure: std::cell::Cell::new(Some(f)) }
285 }
286}
287
288impl<F> fmt::Display for DisplayOnce<F>
289where
290 F: FnOnce(&mut fmt::Formatter) -> fmt::Result
291{
292 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
293 match self.closure.replace(None).take() {
294 Some(closure) => closure(f),
295 None => Ok(())
296 }
297 }
298}
299
300/// Wrapper implementing Display for every closure with matching signature
301///
302/// This wrapper implements Display for every closure implementing
303/// `Fn(&mut fmt::Formatter) -> fmt::Result`.
304///
305/// Can be create using [`lazy_fomat`][lazy_fomat]
306pub struct DisplayFn<F>(F);
307
308impl<F> DisplayFn<F>
309where
310 F: Fn(&mut fmt::Formatter) -> fmt::Result
311{
312 /// Creates an object which `Display::fmt` impl will call this closure.
313 pub fn new(f: F) -> Self { Self(f) }
314}
315
316
317impl<F> fmt::Display for DisplayFn<F>
318where
319 F: Fn(&mut fmt::Formatter) -> fmt::Result
320{
321 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
322 (self.0)(f)
323 }
324}
325
326
327/// Writes to a specified writer. Analogous to `write!`.
328///
329/// See the crate root for general help on the syntax.
330///
331/// The first argument should be something that implements either `io::Write`
332/// or `fmt::Write`. This expression will be evaluated once.
333///
334/// The list of things to write should be written after
335/// the first comma, without any further delimiters.
336///
337/// # Return value
338///
339/// This macro returns `io::Result<()>` or `fmt::Result`,
340/// just as `write!` from `std`.
341///
342/// # Examples
343///
344/// ```
345/// # use fomat_macros::wite;
346/// # fn main() {
347/// use ::std::io::Write;
348/// use ::std::io::BufWriter;
349/// let mut v = vec![];
350/// let world = "World";
351/// wite!(v, "Hello, "(world)"!").unwrap();
352/// wite!(BufWriter::new(&mut v), " "(2+2)).unwrap();
353/// assert_eq!(v, "Hello, World! 4".as_bytes());
354/// # }
355/// ```
356#[macro_export]
357macro_rules! wite {
358 // single tt rules ---------------------------------------------------------
359 (@one $w:ident, ($e:expr)) => { ::std::fmt::Display::fmt(&$e, $w) };
360 (@one $w:ident, [$e:expr]) => { ::std::fmt::Debug::fmt(&$e, $w) };
361 (@one $w:ident, {$e:tt : $($fmt:tt)*}) => {
362 write!($w, concat!("{:", $crate::wite!(@stringify-dense $($fmt)*), "}"), $e)
363 };
364 (@one $w:ident, {$($arg:tt)*}) => {
365 write!($w, $($arg)*)
366 };
367 (@one $w:ident, $string:tt) => { $w.write_str(concat!($string)) };
368
369 (@stringify-dense) => { "" };
370 (@stringify-dense $($tt:tt)+) => { concat!( $(stringify!($tt)),+ ) };
371
372 // expression parsing (manually, because we can't use :expr before `{`)
373 (@expr.. $w:ident {$($before:tt)*} ($($e:tt)*) {$($block:tt)*} $($rest:tt)* ) => {
374 $crate::wite!(@rec $w, $($before)* ($($e)*) {$($block)*} $($rest)*)
375 };
376 (@expr.. $w:ident {$($before:tt)*} ($($expr:tt)*) $tt:tt $($rest:tt)* ) => {
377 $crate::wite!(@expr.. $w {$($before)*} ($($expr)* $tt) $($rest)*)
378 };
379 (@expr $w:ident {$($before:tt)*} ($($expr:tt)*) $tt:tt $($rest:tt)* ) => {
380 $crate::wite!(@expr.. $w {$($before)*} ($($expr)* $tt) $($rest)*)
381 };
382
383 // recursive parsing -------------------------------------------------------
384 // for
385 (@rec $w:ident,
386 for $p:pat in ($e:expr) { $($body:tt)* }
387 sep { $($sep:tt)* }
388 $($rest:tt)*
389 ) => {
390 {
391 let mut first_iteration = true;
392 for $p in $e {
393 if first_iteration {
394 first_iteration = false;
395 } else {
396 $crate::wite!(@rec $w, $($sep)*);
397 }
398 $crate::wite!(@rec $w, $($body)*);
399 }
400 $crate::wite!(@rec $w, $($rest)*);
401 }
402 };
403 (@rec $w:ident,
404 for $p:pat in ($e:expr) { $($body:tt)* }
405 separated { $($sep:tt)* }
406 $($rest:tt)*
407 ) => {
408 $crate::wite!(@rec $w, for $p in ($e) { $($body)* } sep { $($sep)* }$($rest)*)
409 };
410 (@rec $w:ident, for $p:pat in ($e:expr) { $($body:tt)* } $($rest:tt)*) => {
411 $crate::wite!(@rec $w, for $p in ($e) { $($body)* } sep {} $($rest)*)
412 };
413 (@rec $w:ident, for $p:pat in $($tt:tt)* ) => {
414 $crate::wite!(@expr $w { for $p in } () $($tt)*)
415 };
416
417 // match
418 (@rec $w:ident,
419 match ($e:expr) {
420 $( $($p:pat)|+ $(if $g:expr)* => { $($body:tt)* } )*
421 }
422 $($rest:tt)*
423 ) => {
424 {
425 match $e {
426 $(
427 $($p)|+ $(if $g)* => {
428 $crate::wite!(@rec $w, $($body)*)
429 }
430 )*
431 }
432 $crate::wite!(@rec $w, $($rest)*);
433 }
434 };
435 (@rec $w:ident, match $($tt:tt)* ) => {
436 $crate::wite!(@expr $w { match } () $($tt)*)
437 };
438
439 // if let
440 (@rec $w:ident,
441 if let $p:pat = ($e:expr) { $($then:tt)* }
442 else { $($els:tt)* }
443 $($rest:tt)*
444 ) => {
445 {
446 if let $p = $e {
447 $crate::wite!(@rec $w, $($then)*);
448 } else {
449 $crate::wite!(@rec $w, $($els)*);
450 }
451 $crate::wite!(@rec $w, $($rest)*);
452 }
453 };
454 (@rec $w:ident,
455 if let $p:pat = ($e:expr) { $($then:tt)* }
456 else if $($rest:tt)*
457 ) => {
458 $crate::wite!(@ifelseerror)
459 };
460 (@rec $w:ident,
461 if let $p:pat = ($e:expr) { $($then:tt)* }
462 $($rest:tt)*
463 ) => {
464 $crate::wite!(@rec $w, if let $p = ($e) { $($then)* } else {} $($rest)*);
465 };
466 (@rec $w:ident, if let $p:pat = $($tt:tt)* ) => {
467 $crate::wite!(@expr $w { if let $p = } () $($tt)*)
468 };
469
470 // if
471 (@rec $w:ident,
472 if ($cond:expr) { $($then:tt)* }
473 else { $($els:tt)* }
474 $($rest:tt)*
475 ) => {
476 {
477 if $cond {
478 $crate::wite!(@rec $w, $($then)*);
479 } else {
480 $crate::wite!(@rec $w, $($els)*);
481 }
482 $crate::wite!(@rec $w, $($rest)*);
483 }
484 };
485 (@rec $w:ident,
486 if ($cont:expr) { $($then:tt)* }
487 else if $($rest:tt)*
488 ) => {
489 $crate::wite!(@ifelseerror)
490 };
491 (@rec $w:ident, if ($cond:expr) { $($then:tt)* } $($rest:tt)* ) => {
492 $crate::wite!(@rec $w, if ($cond) { $($then)* } else {} $($rest)*);
493 };
494 (@rec $w:ident, if $($tt:tt)* ) => {
495 $crate::wite!(@expr $w { if } () $($tt)*)
496 };
497
498 // equal-sign debugging
499 (@rec $w:ident, (= $e:expr) $($rest:tt)*) => {
500 $crate::wite!(@rec $w, (concat!(stringify!($e), " = ")) ($e) $($rest)*)
501 };
502 (@rec $w:ident, [= $e:expr] $($rest:tt)*) => {
503 $crate::wite!(@rec $w, (concat!(stringify!($e), " = ")) [$e] $($rest)*)
504 };
505 (@rec $w:ident, {= $e:tt : $($fmt:tt)*} $($rest:tt)*) => {
506 $crate::wite!(@rec $w, (concat!(stringify!($e), " = ")) {$e : $($fmt)*} $($rest)*)
507 };
508
509 // single tt
510 (@rec $w:ident, $part:tt $($rest:tt)*) => {
511 {
512 match $crate::wite!(@one $w, $part) {
513 Ok(_) => (),
514 error => return error,
515 }
516 $crate::wite!(@rec $w, $($rest)*);
517 }
518 };
519
520 // terminator
521 (@rec $w:ident, ) => { () };
522
523 (@ifelseerror) => {
524 {
525 let ERROR: () = "`else if` is not supported";
526 let NOTE: () = "use `match` or `else { if ... }` instead";
527 }
528 };
529
530 // entry point -------------------------------------------------------------
531 ($writer:expr, $($part:tt)*) => {
532 write!(
533 $writer,
534 "{}",
535 $crate::DisplayOnce::new(|f| {
536 $crate::wite!(@rec f, $($part)*);
537 Ok(())
538 })
539 )
540 };
541}
542
543/// Writes to a specified writer, with an appended newline. Analogous to `writeln!`.
544///
545/// See the documentation for [`wite!`](macro.wite.html).
546///
547/// When there are no arguments, the comma may be omitted.
548///
549/// # Examples
550///
551/// ```no_run
552/// # use fomat_macros::witeln;
553/// # fn main() {
554/// # use ::std::io::Write;
555/// # let mut file = vec![];
556/// witeln!(file).unwrap();
557/// witeln!(file, "Hi").unwrap();
558/// # }
559/// ```
560#[macro_export]
561macro_rules! witeln {
562 ($writer:expr, $($arg:tt)*) => { $crate::wite!($writer, $($arg)* "\n") };
563 ($writer:expr) => { $crate::wite!($writer, "\n") };
564}
565
566/// Prints to stdout. Analogous to `print!`.
567///
568/// See the crate root for general help on the syntax.
569///
570/// # Return value
571///
572/// The macro returns `()`.
573///
574/// # Panics
575///
576/// The macro panics when printing was not successful.
577///
578/// # Behaviour in `#[test]`
579///
580/// The behaviour when testing is similar to `print!`:
581/// the output of this macro will be captured by the
582/// testing framework (meaning that by default `cargo test`
583/// won't show the output).
584///
585/// The only limitation and difference from `print!` is
586/// that when `pint!` is called by a different crate
587/// than the one being tested, the output won't be captured
588/// and will allways be printed to stdout.
589///
590/// # Examples
591///
592/// ```no_run
593/// # use fomat_macros::pint;
594/// # fn main() {
595/// pint!("four = "(2+2));
596/// # }
597/// ```
598#[macro_export]
599macro_rules! pint {
600 ($($arg:tt)*) => {
601 {
602 {
603 #[cfg(not(test))] {
604 use ::std::io::Write;
605 let o = ::std::io::stdout();
606 $crate::wite!(o.lock(), $($arg)*).unwrap();
607 }
608 #[cfg(test)] {
609 print!("{}", $crate::fomat!($($arg)*))
610 }
611 }
612 }
613 }
614}
615
616/// Prints to stdout, with an appended newline. Analoguous to `println!`.
617///
618/// See the docs for [`print!`](macro.pint.html) for more details.
619///
620/// # Examples
621///
622/// ```no_run
623/// # use fomat_macros::pintln;
624/// # fn main() {
625/// pintln!();
626/// pintln!((2 * 2));
627/// # }
628/// ```
629#[macro_export]
630macro_rules! pintln {
631 ($($arg:tt)*) => {
632 {
633 #[cfg(not(test))] {
634 $crate::pint!($($arg)* "\n")
635 }
636 #[cfg(test)] {
637 print!("{}", fomat!($($arg)* "\n"))
638 }
639 }
640 }
641}
642
643/// Prints to stderr. Analogous to `eprint!`.
644///
645/// See the crate root for general help on the syntax.
646///
647/// # Return value
648///
649/// None
650///
651/// # Panics
652///
653/// This macro, in contrary to `pint!`, silently ignores
654/// all errors.
655///
656/// # Examples
657///
658/// ```no_run
659/// # use fomat_macros::epint;
660/// # fn main() {
661/// epint!("foo")
662/// # }
663/// ```
664#[macro_export]
665macro_rules! epint {
666 ($($arg:tt)*) => {
667 {
668 use ::std::io::Write;
669 let o = ::std::io::stderr();
670 $crate::wite!(o.lock(), $($arg)*).unwrap();
671 }
672 }
673}
674
675/// Same as `epint`
676#[macro_export]
677#[deprecated(since="0.2.1", note="use `epint` instead")]
678macro_rules! perr { ($($arg:tt)*) => { $crate::epint!($($arg)*) } }
679
680/// Prints to stderr, with an appended newline. Analogous to `eprintln!`.
681///
682/// See the docs for [`epint!`](macro.epint.html) for more info.
683///
684/// # Examples
685///
686/// ```no_run
687/// # use fomat_macros::epintln;
688/// # fn main() {
689/// let x = 3;
690/// epintln!((=x));
691/// # }
692/// ```
693#[macro_export]
694macro_rules! epintln {
695 ($($arg:tt)*) => { $crate::epint!($($arg)* "\n") }
696}
697
698/// Same as `epintln`
699#[macro_export]
700#[deprecated(since="0.2.1", note="use `epint` instead")]
701macro_rules! perrln { ($($arg:tt)*) => { $crate::epintln!($($arg)*) } }
702
703/// Creates a formatted string. Analogous to `format!`.
704///
705/// See the crate root for general help on the syntax.
706///
707/// This macro returns `String` containing the formatted text.
708///
709/// # Panics
710///
711/// The macro will panic if formatting fails (which shoudn't happen for any
712/// of `std` types).
713///
714/// # Examples
715///
716/// ```
717/// # use fomat_macros::fomat;
718/// # fn main() {
719/// let v = [1, 2];
720///
721/// let s = fomat!("Hello, "[v]);
722/// assert_eq!(s, "Hello, [1, 2]");
723///
724/// let s = fomat!(for x in &v { (x*x) ";" });
725/// assert_eq!(s, "1;4;");
726/// # }
727/// ```
728#[macro_export]
729macro_rules! fomat {
730 // capacity estimation -----------------------------------------------------
731 (@cap ($len:expr, $multiplier:expr)) => {
732 ($len, $multiplier)
733 };
734
735 // skip all irrelevant tts and conditional bodies
736 (@cap ($($lm:tt)*) for $p:pat in $($tt:tt)*) => {
737 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
738 };
739 (@cap ($($lm:tt)*) sep $($tt:tt)*) => {
740 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
741 };
742 (@cap ($($lm:tt)*) separated $($tt:tt)*) => {
743 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
744 };
745 (@cap ($($lm:tt)*) if let $p:pat = $($tt:tt)*) => {
746 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
747 };
748 (@cap ($($lm:tt)*) if $($tt:tt)*) => {
749 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
750 };
751 (@cap ($($lm:tt)*) else $($tt:tt)*) => {
752 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
753 };
754 (@cap ($($lm:tt)*) match $($tt:tt)*) => {
755 $crate::fomat!(@cap-ignore ($($lm)*) $($tt)*)
756 };
757
758 // When there's any unconditional string interpolation,
759 // we multiply the initial capacity by 2
760 // (which would probably happen anyway).
761 (@cap ($len:expr, $mul:expr) ($($x:tt)*) $($rest:tt)*) => {
762 $crate::fomat!(@cap ($len, 2) $($rest)*)
763 };
764 (@cap ($len:expr, $mul:expr) [$($x:tt)*] $($rest:tt)*) => {
765 $crate::fomat!(@cap ($len, 2) $($rest)*)
766 };
767 (@cap ($len:expr, $mul:expr) {$($x:tt)*} $($rest:tt)*) => {
768 $crate::fomat!(@cap ($len, 2) $($rest)*)
769 };
770
771 // Now the only legal tt is a string literal
772 (@cap ($len:expr, $mul:expr) $string:tt $($rest:tt)*) => {
773 // Concat forces the token to be a string literal.
774 $crate::fomat!(@cap ($len + concat!($string).len(), $mul) $($rest)*)
775 };
776
777 // Ignores everything till after next block
778 (@cap-ignore ($($lm:tt)*) { $($block:tt)* } $($rest:tt)*) => {
779 $crate::fomat!(@cap ($($lm)*) $($rest)*)
780 };
781 (@cap-ignore ($($lm:tt)*) $tt:tt $($rest:tt)*) => {
782 $crate::fomat!(@cap-ignore ($($lm)*) $($rest)*)
783 };
784
785 // entry points ------------------------------------------------------------
786 () => { String::new() };
787 ($($arg:tt)*) => {
788 {
789 use ::std::fmt::Write;
790 let (len, mul) = $crate::fomat!(@cap (0, 1) $($arg)*);
791 let mut _s = String::with_capacity(len * mul);
792 $crate::wite!(_s, $($arg)*).ok();
793 _s
794 }
795 }
796}
797
798/// Creates a displayable object based on its arguments.
799///
800/// This macro works in a similar way to [`fomat`](fomat),
801/// but instead of `String` it returns an object ([`DisplayFn`](DisplayFn))
802/// that implements [`Display`][std::fmt::Display] and can be printed later
803/// (using `format`, `fomat` or calling `Display::fmt` directly).
804///
805/// See the [crate root](crate) for general help on the syntax.
806///
807/// Prefix the arguments with `move` to force moving all variables
808/// (can help when `'static` bound is required).
809///
810/// # Examples
811///
812/// Direct usage
813///
814/// ```
815/// # use fomat_macros::{fomat, lazy_fomat};
816/// let fence = lazy_fomat!(for _ in 0..5 { "-" });
817/// let s = fomat!((fence)" hello "(fence));
818///
819/// assert_eq!(s, "----- hello -----");
820/// ```
821///
822/// Returning `impl Display`
823///
824/// ```
825/// # use fomat_macros::lazy_fomat;
826/// fn greet(name: String) -> impl ::std::fmt::Display {
827/// lazy_fomat!(move "Hello, "(name)"!")
828/// }
829///
830/// assert_eq!(greet("World".into()).to_string(), "Hello, World!");
831/// ```
832#[macro_export]
833macro_rules! lazy_fomat {
834 (move $($arg:tt)*) => {
835 $crate::DisplayFn::new(move |f| {
836 $crate::wite!(@rec f, $($arg)*);
837 Ok(())
838 })
839 };
840 ($($arg:tt)*) => {
841 $crate::DisplayFn::new(|f| {
842 $crate::wite!(@rec f, $($arg)*);
843 Ok(())
844 })
845 };
846}
847
848#[test]
849fn basics() {
850 let world = "World";
851 assert_eq!(fomat!("Hello, "(world)"!"), "Hello, World!");
852 let x = 3;
853 assert_eq!(fomat!((x)" * 2 = "(x * 2)), "3 * 2 = 6");
854}
855
856#[test]
857fn empty() {
858 assert_eq!(fomat!(), "");
859}
860
861#[test]
862fn debug() {
863 let v = [1,2,3];
864 assert_eq!(fomat!([v] "."), "[1, 2, 3].");
865}
866
867#[test]
868fn test_if() {
869 let s = fomat!(
870 if true { "A" "A" } else { "X" }
871 if false { "X" } else { "D" "D" }
872 if true { "T" "T" }
873 if false { "X" }
874 if let Some(x) = Some(5) { (x) (x) } else { "E" "E" }
875 if let None = Some(5) { "X" } else { "F" "F" }
876 if let Some(x) = Some(5) { (x) }
877 if let None = Some(5) { "X" }
878 if {let t = true; t} { "K" }
879 "."
880 );
881 assert_eq!(s, "AADDTT55FF5K.");
882}
883
884#[test]
885fn format() {
886 assert_eq!( fomat!({5:02}), "05" );
887 assert_eq!( fomat!({"{}-{}", 4, 2}), "4-2" );
888}
889
890#[test]
891fn separator() {
892 let v = [1, 2, 3];
893 let s1 = fomat!( for x in &v { (x) } separated { "-" "-" } "." );
894 let s2 = fomat!( for x in &v { (x) } sep { "--" } "." );
895 assert_eq!(s1, "1--2--3.");
896 assert_eq!(s2, "1--2--3.");
897}
898
899#[test]
900fn test_match() {
901 let s = fomat!(
902 match Some(5) {
903 Some(x) if x > 3 => { (x) "!" }
904 Some(2) | None => {}
905 _ => {}
906 }
907 "."
908 );
909 assert_eq!(s, "5!.");
910}
911
912#[test]
913fn capacity() {
914 assert_eq!(fomat!("Hello, " "world!").capacity(), 13);
915 assert_eq!(fomat!("Hello, "[40+2]).capacity(), 14);
916 let s = fomat!(
917 "Hello"
918 for x in [1][1..].iter() { (x) "a" }
919 if let Some(()) = None { "b" }
920 if false { "c" } else {}
921 match 1 { 2 => { "e" } _ => {} }
922 "!"
923 );
924 assert_eq!(s.capacity(), 6);
925}
926
927#[test]
928fn fmt_write() {
929 use std::fmt;
930 struct Foo;
931
932 impl fmt::Display for Foo {
933 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
934 wite!(f, "foo"(42))
935 }
936 }
937
938 assert_eq!(format!("{}", Foo), "foo42");
939}
940
941#[test]
942fn equal_sign() {
943 let x = 5;
944 let v = [10];
945 assert_eq!(fomat!((=x) "."), "x = 5.");
946 assert_eq!(fomat!([=&v] "."), "&v = [10].");
947 assert_eq!(fomat!({=13:05b} "."), "13 = 01101.");
948}
949
950#[test]
951fn depth() {
952 let _ = fomat!(
953 "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
954 "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
955 "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
956 "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
957 "1" "2" "3" "4" "5" "6" "7" "8" "9" "0"
958 );
959}
960
961#[test]
962fn non_static_writer() {
963 use std::io::Write;
964 use std::io::Result;
965 use std::fmt::Arguments;
966
967 struct Prepender<'a, T: Write> {
968 prefix: &'a str,
969 writer: T,
970 }
971
972 impl<'a, T: Write> Write for Prepender<'a, T> {
973 fn write(&mut self, buf: &[u8]) -> Result<usize> {
974 self.writer.write(buf)
975 }
976
977 fn flush(&mut self) -> Result<()> {
978 self.writer.flush()
979 }
980
981 fn write_fmt(&mut self, fmt: Arguments) -> Result<()> {
982 self.writer.write_all(self.prefix.as_bytes())?;
983 self.writer.write_fmt(fmt)
984 }
985 }
986
987 let mut buf = vec![];
988 witeln!(
989 Prepender { prefix: &"foo ".to_owned(), writer: &mut buf },
990 (2+2)
991 ).unwrap();
992 assert_eq!(buf, "foo 4\n".as_bytes());
993}
994
995#[test]
996fn no_semicolon() {
997 if true { pint!("foo") } else { epint!("bar") }
998 pintln!("foo" "bar")
999}
1000
1001#[test]
1002fn move_and_borrow() {
1003 // Test if fomat! arguments can move some and borrow other variables.
1004 let iter = vec![1, 2, 3].into_iter();
1005 let borrow_me = vec![1, 2, 3];
1006 let s = fomat!(for x in iter { (x) } (borrow_me.len()));
1007 assert_eq!(s, "1233");
1008 assert_eq!(borrow_me, [1, 2, 3]);
1009}