├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── bench.rs ├── src └── lib.rs └── tests ├── capturing.rs └── extern.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | .*.swp 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fomat-macros" 3 | version = "0.3.2" 4 | authors = ["Michał Krasnoborski "] 5 | 6 | description = "Alternative syntax for print/write/format-like macros with a small templating language" 7 | 8 | repository = "https://github.com/krdln/fomat-macros" 9 | readme = "README.md" 10 | keywords = ["macro", "print", "template", "format", "interpolation"] 11 | license = "MIT" 12 | 13 | [dev-dependencies] 14 | tar = "0.4.9" 15 | tempfile = "3.3.0" 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Michał Krasnoborski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fomat-macros 2 | 3 | [**documentation**](https://docs.rs/fomat-macros), 4 | [**crate**](https://crates.io/crates/fomat-macros) 5 | 6 | This crate provides alternative syntax for 7 | `write!`, `writeln!`, `print!`, `println!`, `eprint!`, `eprintln!` and `format!` macros 8 | from the [Rust](https://www.rust-lang.org/) standard library. 9 | 10 | The names of macros in this crate 11 | are formed by removing the letter `r` from their `std` counterparts: 12 | `wite!`, `witeln!`, `pint!`, `pintln!`, 13 | `epint!`, `epintln!`, `fomat!`. 14 | 15 | ## Installation 16 | 17 | Add this to your `Cargo.toml`: 18 | 19 | ```toml 20 | [dependencies] 21 | fomat-macros = "0.3.2" 22 | ``` 23 | 24 | And `use` the macros in your `.rs` file, eg.: 25 | 26 | ```rust 27 | use fomat_macros::pintln; 28 | ``` 29 | 30 | This version requires Rust 1.30. For support for older versions, 31 | [see version 0.2.1](https://github.com/krdln/fomat-macros/tree/v0.2.1). 32 | 33 | ## Examples 34 | 35 | ```rust 36 | pintln!("Hello, world!"); 37 | pintln!(); // empty line 38 | pintln!("The answer is "(40 + 2)); // parentheses use the Display trait 39 | pintln!([vec![1, 2, 3]] " -- numbers"); // brackets use the Debug trait 40 | ``` 41 | 42 | As you can see, instead the format string and arguments, 43 | we have a list of *things* to print 44 | without any separators. 45 | Each *thing* may be a string literal or an expression in brackets 46 | (apart of `()` and `[]` there are also braces `{}`, which may 47 | be used for more advanced format specifiers, see the docs). 48 | 49 | You can also use `if`, `if let`, `match` and `for` constructs 50 | inside the macro. They use regular Rust syntax, except 51 | everything inside the `{}` blocks will use the 52 | list-of-things-to-print syntax again. 53 | 54 | ```rust 55 | let list = vec![1, 2, 3]; 56 | let s = fomat!( for x in &list { (x) " :: " } "nil" ); 57 | // s == "1 :: 2 :: 3 :: nil" 58 | ``` 59 | 60 | For loops can also use an optional separator. 61 | [For details, see the docs.](https://docs.rs/fomat-macros/0.2/fomat_macros/#for-loops) 62 | 63 | There's also a shorthand for debugging, which prints both 64 | the expression and value. To enable, put `=` as the first 65 | character inside the any kind of brackets. 66 | 67 | ```rust 68 | let list = vec![1, 2, 3]; 69 | epintln!([=list]); // prints list = [1, 2, 3] 70 | ``` 71 | 72 | ## Why? 73 | 74 | What was the motivation to create this crate? 75 | 76 | * More locality – everything is written in the same order 77 | it will be printed. But that might a personal preference. 78 | * Easier to refactor – especially when you suddenly want 79 | to add a conditional print inside a long format string. Compare: 80 | 81 | ```rust 82 | let s = fomat!( 83 | "first line\n" 84 | if condition() { (foo) } 85 | "third line\n" 86 | ); 87 | ``` 88 | 89 | ```rust 90 | let s = { 91 | use ::std::fmt::Write; 92 | let mut s = "first line\n".to_owned(); 93 | if condition() { write!(s, "{}", foo).unwrap() } 94 | write!(s, "third line\n").unwrap(); 95 | s 96 | }; 97 | ``` 98 | 99 | * Speed! `fomat!` may be faster than `format!` 100 | (see `cargo bench`). That's because there's 101 | just one virtual call for single invocation 102 | of `fomat!` instead of one per each argument 103 | in `std::format!`. 104 | 105 | ## Limitations 106 | 107 | The `write!` and `writeln!` macros work on everything that 108 | has a `.write_fmt` method. This crate requires also 109 | the `.write_str` method. It works for any `io::Write` or 110 | `fmt::Write`, but in unlikely circumstances if you're using something 111 | custom, you should consult the source. 112 | 113 | ## Is it a templating language? 114 | 115 | Kind of, but please don't use it as HTML-templating language 116 | for security critical code, as it performs no escaping 117 | of special characters. 118 | 119 | -------------------------------------------------------------------------------- /benches/bench.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate test; 4 | 5 | #[macro_use] extern crate fomat_macros; 6 | 7 | use test::{Bencher, black_box}; 8 | 9 | #[bench] 10 | fn literal_fomat(b: &mut Bencher) { 11 | b.iter(|| fomat!("Hello")) 12 | } 13 | 14 | #[bench] 15 | fn literal_format(b: &mut Bencher) { 16 | b.iter(|| format!("Hello")) 17 | } 18 | 19 | #[bench] 20 | fn short_fomat(b: &mut Bencher) { 21 | let world = "world"; 22 | b.iter(|| fomat!("Hello, "(black_box(world))"!") ) 23 | } 24 | 25 | #[bench] 26 | fn short_format(b: &mut Bencher) { 27 | let world = "world"; 28 | b.iter(|| format!("Hello, {}!", black_box(world)) ) 29 | } 30 | 31 | #[bench] 32 | fn two_fomat(b: &mut Bencher) { 33 | b.iter(|| fomat!("One"(3)(2+2))) 34 | } 35 | 36 | #[bench] 37 | fn two_format(b: &mut Bencher) { 38 | b.iter(|| format!("One{}{}", 3, 2+2)) 39 | } 40 | 41 | #[bench] 42 | fn three_fomat(b: &mut Bencher) { 43 | b.iter(|| fomat!("One"(2)(3)(2+2))) 44 | } 45 | 46 | #[bench] 47 | fn three_format(b: &mut Bencher) { 48 | b.iter(|| format!("One{}{}{}", 2, 3, 2+2)) 49 | } 50 | 51 | #[bench] 52 | fn long_fomat(b: &mut Bencher) { 53 | let world = "world"; 54 | b.iter(|| fomat!( 55 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis 56 | ligula nibh, at maximus diam eleifend sit amet. Suspendisse mattis 57 | nisi quis eleifend commodo. Praesent ut dui luctus, ullamcorper felis 58 | et, dictum purus. Nullam elit nunc, dictum ornare arcu vitae, suscipit 59 | pulvinar felis. Donec malesuada sollicitudin arcu, sit amet laoreet 60 | dolor elementum eu. Cras quam augue, feugiat ac imperdiet vel, posuere 61 | et elit. Nunc consequat bibendum dolor at auctor. Cras vel urna 62 | fermentum, viverra est ut, posuere elit. Suspendisse mattis aliquam 63 | tincidunt. 64 | 65 | Pellentesque ac risus eu nunc molestie cursus id in risus. 66 | Pellentesque vel ipsum sed lectus vehicula fermentum vitae vel est. 67 | Phasellus suscipit dolor eu finibus tincidunt. Duis interdum lectus at 68 | interdum viverra. Phasellus in mi dapibus, condimentum ligula at, 69 | rhoncus quam. Morbi pulvinar tortor sit amet fringilla sodales. Duis 70 | feugiat felis ut ante efficitur, vitae consectetur leo luctus. Vivamus 71 | congue, ex vel ullamcorper auctor, quam dolor vulputate purus, quis 72 | dapibus nulla ipsum in ligula. Sed sed vehicula odio. Quisque bibendum 73 | efficitur sodales. In aliquet sollicitudin venenatis. Sed id euismod 74 | nulla. Cras risus ligula, mattis in arcu eu, pulvinar aliquet metus. 75 | Mauris et convallis eros, in tempus sem." 76 | (world)(1)(2)(3)[vec![4]] "!" 77 | ) ) 78 | } 79 | 80 | #[bench] 81 | fn long_format(b: &mut Bencher) { 82 | let world = "world"; 83 | b.iter(|| format!( 84 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In convallis 85 | ligula nibh, at maximus diam eleifend sit amet. Suspendisse mattis 86 | nisi quis eleifend commodo. Praesent ut dui luctus, ullamcorper felis 87 | et, dictum purus. Nullam elit nunc, dictum ornare arcu vitae, suscipit 88 | pulvinar felis. Donec malesuada sollicitudin arcu, sit amet laoreet 89 | dolor elementum eu. Cras quam augue, feugiat ac imperdiet vel, posuere 90 | et elit. Nunc consequat bibendum dolor at auctor. Cras vel urna 91 | fermentum, viverra est ut, posuere elit. Suspendisse mattis aliquam 92 | tincidunt. 93 | 94 | Pellentesque ac risus eu nunc molestie cursus id in risus. 95 | Pellentesque vel ipsum sed lectus vehicula fermentum vitae vel est. 96 | Phasellus suscipit dolor eu finibus tincidunt. Duis interdum lectus at 97 | interdum viverra. Phasellus in mi dapibus, condimentum ligula at, 98 | rhoncus quam. Morbi pulvinar tortor sit amet fringilla sodales. Duis 99 | feugiat felis ut ante efficitur, vitae consectetur leo luctus. Vivamus 100 | congue, ex vel ullamcorper auctor, quam dolor vulputate purus, quis 101 | dapibus nulla ipsum in ligula. Sed sed vehicula odio. Quisque bibendum 102 | efficitur sodales. In aliquet sollicitudin venenatis. Sed id euismod 103 | nulla. Cras risus ligula, mattis in arcu eu, pulvinar aliquet metus. 104 | Mauris et convallis eros, in tempus sem.{}{}{}{}{:?}!", 105 | world, 1, 2, 3, vec![4] 106 | ) ) 107 | } 108 | -------------------------------------------------------------------------------- /src/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 | 272 | use std::fmt; 273 | 274 | #[doc(hidden)] 275 | pub struct DisplayOnce { 276 | closure: std::cell::Cell> 277 | } 278 | 279 | impl DisplayOnce 280 | where 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 | 288 | impl fmt::Display for DisplayOnce 289 | where 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] 306 | pub struct DisplayFn(F); 307 | 308 | impl DisplayFn 309 | where 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 | 317 | impl fmt::Display for DisplayFn 318 | where 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] 357 | macro_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] 561 | macro_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] 599 | macro_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] 630 | macro_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] 665 | macro_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")] 678 | macro_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] 694 | macro_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")] 701 | macro_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] 729 | macro_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] 833 | macro_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] 849 | fn 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] 857 | fn empty() { 858 | assert_eq!(fomat!(), ""); 859 | } 860 | 861 | #[test] 862 | fn debug() { 863 | let v = [1,2,3]; 864 | assert_eq!(fomat!([v] "."), "[1, 2, 3]."); 865 | } 866 | 867 | #[test] 868 | fn 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] 885 | fn format() { 886 | assert_eq!( fomat!({5:02}), "05" ); 887 | assert_eq!( fomat!({"{}-{}", 4, 2}), "4-2" ); 888 | } 889 | 890 | #[test] 891 | fn 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] 900 | fn 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] 913 | fn 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] 928 | fn 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] 942 | fn 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] 951 | fn 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] 962 | fn 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 { 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] 996 | fn no_semicolon() { 997 | if true { pint!("foo") } else { epint!("bar") } 998 | pintln!("foo" "bar") 999 | } 1000 | 1001 | #[test] 1002 | fn 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 | } 1010 | -------------------------------------------------------------------------------- /tests/capturing.rs: -------------------------------------------------------------------------------- 1 | extern crate tempfile; 2 | extern crate tar; 3 | 4 | use std::path::Path; 5 | 6 | const FILES: &'static [(&'static str, &'static str)] = &[ 7 | ("Cargo.toml", r#" 8 | [package] 9 | name = "pint" 10 | version = "0.1.0" 11 | 12 | [dependencies] 13 | fomat-macros = { path = "PATH" } 14 | "#), 15 | ("src/main.rs", r#" 16 | #[macro_use] extern crate fomat_macros; 17 | 18 | fn foo() { 19 | pint!("stdout"(1)); 20 | pintln!("stdout"(2)); 21 | epint!("stderr"(1)); 22 | epintln!("stderr"(2)); 23 | perr!("stderr"(1)); 24 | perrln!("stderr"(2)); 25 | } 26 | 27 | fn main() { 28 | foo(); 29 | } 30 | 31 | #[test] 32 | fn capturing() { 33 | foo(); 34 | } 35 | "#), 36 | ("src/lib.rs", r#" 37 | #[macro_use] extern crate fomat_macros; 38 | 39 | pub fn called_from_other_crate() { 40 | pintln!("nocapture"(1)); 41 | } 42 | "#), 43 | ("tests/nocapture.rs", r#" 44 | extern crate pint; 45 | 46 | #[test] 47 | fn nocapture() { 48 | pint::called_from_other_crate(); 49 | } 50 | "#), 51 | ]; 52 | 53 | fn unpack_files(directory: &Path, replace_path: &str) { 54 | use tar::{Builder, Header, Archive}; 55 | 56 | let mut builder = Builder::new(Vec::new()); 57 | for &(name, data) in FILES { 58 | let data = data.replace("PATH", replace_path); 59 | let mut header = Header::new_gnu(); 60 | header.set_path(name).unwrap(); 61 | header.set_size(data.len() as u64); 62 | header.set_cksum(); 63 | builder.append(&header, data.as_bytes()).unwrap(); 64 | } 65 | 66 | let archive: &[u8] = &builder.into_inner().unwrap(); 67 | Archive::new(archive).unpack(directory) 68 | .expect("Can't unpack test crate"); 69 | } 70 | 71 | #[test] 72 | fn capturing() { 73 | use std::process::Command; 74 | use std::env; 75 | use std::str::from_utf8; 76 | 77 | let rootdir = { 78 | let mut rootdir = env::current_exe().unwrap(); 79 | while rootdir.file_name() != Some("target".as_ref()) { 80 | assert!( rootdir.pop() ); 81 | } 82 | assert!( rootdir.pop() ); 83 | rootdir 84 | }; 85 | 86 | let pintdir = tempfile::Builder::new() 87 | .prefix("fomat-macros-capturing-test") 88 | .tempdir() 89 | .expect("Can't create tempdir"); 90 | unpack_files(pintdir.as_ref(), rootdir.to_str().unwrap()); 91 | 92 | assert!( 93 | Command::new("cargo").arg("build") 94 | .current_dir(&pintdir) 95 | .status().unwrap().success() 96 | ); 97 | 98 | let expected_stdout = "stdout1stdout2\n"; 99 | let expected_stderr = "stderr1stderr2\nstderr1stderr2\n"; 100 | 101 | let output = Command::new("target/debug/pint") 102 | .current_dir(&pintdir) 103 | .output().unwrap(); 104 | assert!(output.status.success()); 105 | assert_eq!(from_utf8(&output.stdout).unwrap(), expected_stdout); 106 | assert_eq!(from_utf8(&output.stderr).unwrap(), expected_stderr); 107 | 108 | let output = Command::new("cargo") 109 | .arg("test") 110 | .current_dir(&pintdir) 111 | .output().unwrap(); 112 | assert!(output.status.success()); 113 | assert!(!from_utf8(&output.stdout).unwrap().contains(expected_stdout)); 114 | assert!(from_utf8(&output.stderr).unwrap().contains(expected_stderr)); 115 | assert!(from_utf8(&output.stdout).unwrap().contains("nocapture1")); 116 | 117 | let output = Command::new("cargo") 118 | .args(&["test", "--", "--nocapture"]) 119 | .current_dir(&pintdir) 120 | .output().unwrap(); 121 | assert!(output.status.success()); 122 | assert!(from_utf8(&output.stdout).unwrap().contains(expected_stdout)); 123 | assert!(from_utf8(&output.stderr).unwrap().contains(expected_stderr)); 124 | } 125 | -------------------------------------------------------------------------------- /tests/extern.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] extern crate fomat_macros; 2 | 3 | #[test] 4 | fn macro_use() { 5 | let c: Vec = vec!["a".into()]; 6 | let _ = fomat!( 7 | "
    " 8 | for x in &c { "
  • " (x) "
  • " } 9 | "
" 10 | ); 11 | } 12 | --------------------------------------------------------------------------------