genco/lib.rs
1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/genco-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/genco)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/genco.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/genco)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-genco-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/genco)
4//!
5//! A whitespace-aware quasiquoter for beautiful code generation.
6//!
7//! Central to genco are the [`quote!`] and [`quote_in!`] procedural macros which
8//! ease the construction of [token streams].
9//!
10//! This project solves the following language-specific concerns:
11//!
12//! * **Imports** — Generates and groups [import statements] as they are used.
13//! So you only import what you use, with no redundancy. We also do our best
14//! to [solve namespace conflicts].
15//!
16//! * **String Quoting** — genco knows how to [quote strings]. And can even
17//! [interpolate] values *into* the quoted string if it's supported by the
18//! language.
19//!
20//! * **Structural Indentation** — The quoter relies on intuitive
21//! [whitespace detection] to structurally sort out spacings and indentation.
22//! Allowing genco to generate beautiful readable code with minimal effort.
23//! This is also a requirement for generating correctly behaving code in
24//! languages like Python where [indentation is meaningful].
25//!
26//! * **Language Customization** — Building support for new languages is a
27//! piece of cake with the help of the [impl_lang!] macro.
28//!
29//! <br>
30//!
31//! To support line changes during [whitespace detection], we depend on span
32//! information which was made available in Rust `1.88`. Before that, we rely on
33//! a nightly [`proc_macro_span` feature] to work.
34//!
35//! *Prior to this version of Rust* if you want fully functional whitespace
36//! detection you must build and run projects using genco with a `nightly`
37//! compiler. This is important for whitespace-sensitive languages like python.
38//!
39//! You can try the difference between:
40//!
41//! ```bash
42//! cargo run --example rust
43//! ```
44//!
45//! And:
46//!
47//! ```bash
48//! cargo +nightly run --example rust
49//! ```
50//!
51//! [`proc_macro_span` feature]: https://github.com/rust-lang/rust/issues/54725
52//!
53//! <br>
54//!
55//! ## Supported Languages
56//!
57//! The following are languages which have built-in support in genco.
58//!
59//! * [🦀 <b>Rust</b>][rust]<br>
60//! <small>[Example][rust-example]</small>
61//!
62//! * [☕ <b>Java</b>][java]<br>
63//! <small>[Example][java-example]</small>
64//!
65//! * [🎼 <b>C#</b>][c#]<br>
66//! <small>[Example][c#-example]</small>
67//!
68//! * [🐿️ <b>Go</b>][go]<br>
69//! <small>[Example][go-example]</small>
70//!
71//! * [🎯 <b>Dart</b>][dart]<br>
72//! <small>[Example][dart-example]</small>
73//!
74//! * [🌐 <b>JavaScript</b>][js]<br>
75//! <small>[Example][js-example]</small>
76//!
77//! * [🇨 <b>C</b>][c]<br>
78//! <small>[Example][c-example]</small>
79//!
80//! * [🐍 <b>Python</b>][python]<br>
81//! <small>[Example][python-example]</small>
82//!
83//! <small>Is your favorite language missing? <b>[Open an issue!]</b></small>
84//!
85//! You can run one of the examples by:
86//!
87//! ```bash
88//! cargo +nightly run --example rust
89//! ```
90//!
91//! <br>
92//!
93//! ## Rust Example
94//!
95//! The following is a simple program producing Rust code to stdout with custom
96//! configuration:
97//!
98//! ```rust,no_run
99//! use genco::prelude::*;
100//!
101//! let hash_map = rust::import("std::collections", "HashMap");
102//!
103//! let tokens: rust::Tokens = quote! {
104//! fn main() {
105//! let mut m = $hash_map::new();
106//! m.insert(1u32, 2u32);
107//! }
108//! };
109//!
110//! println!("{}", tokens.to_file_string()?);
111//! # Ok::<_, genco::fmt::Error>(())
112//! ```
113//!
114//! This would produce:
115//!
116//! ```rust,no_test
117//! use std::collections::HashMap;
118//!
119//! fn main() {
120//! let mut m = HashMap::new();
121//! m.insert(1u32, 2u32);
122//! }
123//! ```
124//!
125//! <br>
126//!
127//! [`quote_in!`]: <https://docs.rs/genco/latest/genco/macro.quote_in.html>
128//! [`quote!`]: <https://docs.rs/genco/latest/genco/macro.quote.html>
129//! [`quoted()`]: <https://docs.rs/genco/latest/genco/tokens/fn.quoted.html>
130//! [c-example]: <https://github.com/udoprog/genco/blob/master/examples/c.rs>
131//! [c]: <https://docs.rs/genco/latest/genco/lang/c/index.html>
132//! [c#-example]: <https://github.com/udoprog/genco/blob/master/examples/csharp.rs>
133//! [c#]: <https://docs.rs/genco/latest/genco/lang/csharp/index.html>
134//! [dart-example]: <https://github.com/udoprog/genco/blob/master/examples/dart.rs>
135//! [dart]: <https://docs.rs/genco/latest/genco/lang/dart/index.html>
136//! [go-example]: <https://github.com/udoprog/genco/blob/master/examples/go.rs>
137//! [go]: <https://docs.rs/genco/latest/genco/lang/go/index.html>
138//! [impl_lang!]: <https://docs.rs/genco/latest/genco/macro.impl_lang.html>
139//! [import statements]: <https://docs.rs/genco/latest/genco/macro.quote.html#imports>
140//! [indentation is meaningful]: <https://docs.python.org/3/faq/design.html#why-does-python-use-indentation-for-grouping-of-statements>
141//! [interpolate]: <https://docs.rs/genco/latest/genco/macro.quote.html#quoted-string-interpolation>
142//! [java-example]: <https://github.com/udoprog/genco/blob/master/examples/java.rs>
143//! [java]: <https://docs.rs/genco/latest/genco/lang/java/index.html>
144//! [js-example]: <https://github.com/udoprog/genco/blob/master/examples/js.rs>
145//! [js]: <https://docs.rs/genco/latest/genco/lang/js/index.html>
146//! [Open an issue!]: <https://github.com/udoprog/genco/issues/new>
147//! [python-example]: <https://github.com/udoprog/genco/blob/master/examples/python.rs>
148//! [python]: <https://docs.rs/genco/latest/genco/lang/python/index.html>
149//! [quote strings]: <https://docs.rs/genco/latest/genco/macro.quote.html#string-quoting>
150//! [rust-example]: <https://github.com/udoprog/genco/blob/master/examples/rust.rs>
151//! [rust]: <https://docs.rs/genco/latest/genco/lang/rust/index.html>
152//! [solve namespace conflicts]: <https://docs.rs/genco/latest/genco/lang/csharp/fn.import.html>
153//! [token streams]: <https://docs.rs/genco/latest/genco/tokens/struct.Tokens.html>
154//! [whitespace detection]: <https://docs.rs/genco/latest/genco/macro.quote.html#whitespace-detection>
155
156#![deny(missing_docs)]
157#![deny(rustdoc::broken_intra_doc_links)]
158#![allow(clippy::needless_doctest_main)]
159#![no_std]
160
161#[cfg(feature = "std")]
162extern crate std;
163
164#[cfg(feature = "alloc")]
165extern crate alloc;
166
167#[cfg(not(feature = "alloc"))]
168compile_error!("genco: The `alloc` feature must be enabled");
169
170/// Whitespace sensitive quasi-quoting.
171///
172/// This and the [`quote_in!`] macro is the thing that this library revolves
173/// around.
174///
175/// It provides a flexible and intuitive mechanism for efficiently generating
176/// beautiful code directly inside of Rust.
177///
178/// > Note that this macro **can only detect line changes** if it's built with
179/// > Rust 1.88 or by using a `nightly` compiler. See the [main genco
180/// > documentation] for more information.
181///
182/// ```
183/// use genco::prelude::*;
184///
185/// let hash_map = &dart::import("dart:collection", "HashMap");
186///
187/// let tokens: dart::Tokens = quote! {
188/// print_greeting(String name) {
189/// print($[str](Hello $(name)));
190/// }
191///
192/// $hash_map<int, String> map() {
193/// return new $hash_map<int, String>();
194/// }
195/// };
196///
197/// println!("{}", tokens.to_file_string()?);
198/// # Ok::<_, genco::fmt::Error>(())
199/// ```
200///
201/// # Interpolation
202///
203/// Variables are interpolated using `$`, so to include the variable `test`, you
204/// would write `$test`. Interpolated variables must implement [`FormatInto`].
205/// Expressions can be interpolated with `$(<expr>)`.
206///
207/// > *Note:* The `$` punctuation itself can be escaped by repeating it twice.
208/// > So `$$` would produce a single `$` token.
209///
210/// ```
211/// use genco::prelude::*;
212///
213/// let hash_map = rust::import("std::collections", "HashMap");
214///
215/// let tokens: rust::Tokens = quote! {
216/// struct Quoted {
217/// field: $hash_map<u32, u32>,
218/// }
219/// };
220///
221/// assert_eq!(
222/// vec![
223/// "use std::collections::HashMap;",
224/// "",
225/// "struct Quoted {",
226/// " field: HashMap<u32, u32>,",
227/// "}",
228/// ],
229/// tokens.to_file_vec()?,
230/// );
231/// # Ok::<_, genco::fmt::Error>(())
232/// ```
233///
234/// <br>
235///
236/// The following is an expression interpolated with `$(<expr>)`.
237///
238/// ```
239/// use genco::prelude::*;
240///
241/// let tokens: genco::Tokens = quote! {
242/// hello $("world".to_uppercase())
243/// };
244///
245/// assert_eq!("hello WORLD", tokens.to_string()?);
246/// # Ok::<_, genco::fmt::Error>(())
247/// ```
248///
249/// <br>
250///
251/// Interpolations are evaluated in the same scope as the macro, so you can
252/// freely make use of Rust operations like the try keyword (`?`) if
253/// appropriate:
254///
255/// ```
256/// use std::error::Error;
257///
258/// use genco::prelude::*;
259///
260/// fn age_fn(age: &str) -> Result<rust::Tokens, Box<dyn Error>> {
261/// Ok(quote! {
262/// fn age() {
263/// println!("You are {} years old!", $(str::parse::<u32>(age)?));
264/// }
265/// })
266/// }
267/// ```
268///
269/// [`FormatInto`]: crate::tokens::FormatInto
270/// [main genco documentation]: https://docs.rs/genco
271///
272/// <br>
273///
274/// # Escape Sequences
275///
276/// Because this macro is *whitespace sensitive*, it might sometimes be
277/// necessary to provide hints of where whitespace should be inserted.
278///
279/// `quote!` trims any trailing and leading whitespace that it sees. So
280/// `quote!(Hello )` is the same as `quote!(Hello)`. To include a space at the
281/// end, we can use the special `$[' ']` escape sequence:
282/// `quote!(Hello$[' '])`.
283///
284/// The available escape sequences are:
285///
286/// * `$[' ']` — Inserts spacing between tokens. This corresponds to the
287/// [Tokens::space] function.
288///
289/// * `$['\r']` — Inserts a push operation. Push operations makes sure that
290/// any following tokens are on their own dedicated line. This corresponds to
291/// the [Tokens::push] function.
292///
293/// * `$['\n']` — Inserts a forced line. Line operations makes sure that any
294/// following tokens have an empty line separating them. This corresponds to
295/// the [Tokens::line] function.
296///
297/// ```
298/// use genco::prelude::*;
299///
300/// let numbers = 3..=5;
301///
302/// let tokens: Tokens<()> = quote!(foo$['\r']bar$['\n']baz$[' ']biz);
303///
304/// assert_eq!("foo\nbar\n\nbaz biz", tokens.to_string()?);
305/// # Ok::<_, genco::fmt::Error>(())
306/// ```
307///
308/// <br>
309///
310/// # String Quoting
311///
312/// Literal strings like `"hello"` are automatically quoted for the target
313/// language according to its [Lang::write_quoted] implementation.
314///
315/// [Lang::write_quoted]: crate::lang::Lang::write_quoted
316///
317/// ```
318/// use genco::prelude::*;
319///
320/// let tokens: java::Tokens = quote! {
321/// "hello world 😊"
322/// $(quoted("hello world 😊"))
323/// $("\"hello world 😊\"")
324/// $[str](hello world $[const]("😊"))
325/// };
326///
327/// assert_eq!(
328/// vec![
329/// "\"hello world \\ud83d\\ude0a\"",
330/// "\"hello world \\ud83d\\ude0a\"",
331/// "\"hello world 😊\"",
332/// "\"hello world \\ud83d\\ude0a\"",
333/// ],
334/// tokens.to_file_vec()?,
335/// );
336/// # Ok::<_, genco::fmt::Error>(())
337/// ```
338///
339/// # Efficient String Quoting
340///
341/// It's worth investigating the different forms of tokens produced by the
342/// above example.
343///
344/// * The first one is a static *quoted string*.
345/// * The second one is a boxed *quoted string*, who's content will be copied
346/// and is stored on the heap.
347/// * The third one is a static *literal* which bypasses language quoting
348/// entirely.
349/// * Finally the fourth one is an interpolated string. They are really neat,
350/// and will be covered more in the next section. It's worth noting that
351/// `$("😊")` is used, because 😊 is not a valid identifier in Rust. So this
352/// example showcases how strings can be directly embedded in an
353/// interpolation.
354///
355/// Here you can see the items produced by the macro.
356///
357/// ```
358/// # use genco::prelude::*;
359/// # let tokens: rust::Tokens = quote! {
360/// # "hello world 😊"
361/// # $(quoted("hello world 😊"))
362/// # $("\"hello world 😊\"")
363/// # $[str](hello world $[const]("😊"))
364/// # };
365/// use genco::tokens::{Item, ItemStr};
366///
367/// assert_eq!(
368/// vec![
369/// Item::open_quote(false),
370/// Item::literal(ItemStr::static_("hello world 😊")),
371/// Item::close_quote(),
372/// Item::push(),
373/// Item::open_quote(false),
374/// Item::literal("hello world 😊".into()),
375/// Item::close_quote(),
376/// Item::push(),
377/// Item::literal(ItemStr::static_("\"hello world 😊\"")),
378/// Item::push(),
379/// Item::open_quote(false),
380/// Item::literal(ItemStr::static_("hello world 😊")),
381/// Item::close_quote()
382/// ],
383/// tokens,
384/// );
385/// # Ok::<_, genco::fmt::Error>(())
386/// ```
387///
388/// <br>
389///
390/// # Quoted String Interpolation
391///
392/// Some languages support interpolating values into strings.
393///
394/// Examples of these are:
395///
396/// * JavaScript - With [template literals] `` `Hello ${a}` `` (note the
397/// backticks).
398/// * Dart - With [interpolated strings] like `"Hello $a"` or `"Hello ${a +
399/// b}"`.
400///
401/// The [`quote!`] macro supports this through `$[str](<content>)`. This will
402/// produce literal strings with the appropriate language-specific quoting and
403/// string interpolation formats used.
404///
405/// Components of the string are runtime evaluated with the typical variable
406/// escape sequences `$ident`, `$(<expr>)`. In order to interpolate the string
407/// at compile time we can instead make use of `$[const](<content>)` like you can see with the smile below:
408///
409/// ```
410/// use genco::prelude::*;
411///
412/// let smile = "😊";
413///
414/// let t: js::Tokens = quote!($[str](Hello $[const](smile) $world));
415/// assert_eq!("`Hello 😊 ${world}`", t.to_string()?);
416/// # Ok::<_, genco::fmt::Error>(())
417/// ```
418///
419/// Interpolated values are specified with `$(<quoted>)`. And `$` itself is
420/// escaped by repeating it twice through `$$`. The `<quoted>` section is
421/// interpreted the same as in the [`quote!`] macro, but is whitespace sensitive.
422/// This means that `$(foo)` is not the same as `$(foo )` since the latter will
423/// have a space preserved at the end.
424///
425/// ```
426/// use genco::prelude::*;
427///
428/// let smile = "😊";
429///
430/// let t: dart::Tokens = quote!($[str](Hello $[const](smile) $(world)));
431/// assert_eq!("\"Hello 😊 $world\"", t.to_string()?);
432///
433/// let t: dart::Tokens = quote!($[str](Hello $[const](smile) $(a + b)));
434/// assert_eq!("\"Hello 😊 ${a + b}\"", t.to_string()?);
435///
436/// let t: js::Tokens = quote!($[str](Hello $[const](smile) $(world)));
437/// assert_eq!("`Hello 😊 ${world}`", t.to_string()?);
438/// # Ok::<_, genco::fmt::Error>(())
439/// ```
440///
441/// <br>
442///
443/// [template literals]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
444/// [interpolated strings]: https://medium.com/run-dart/dart-dartlang-introduction-string-interpolation-8ed99174119a
445///
446/// # Control Flow
447///
448/// [`quote!`] provides some limited mechanisms for control flow inside of the
449/// macro for convenience. The supported mechanisms are:
450///
451/// * [Loops](#loops) - `$(for <bindings> in <expr> [join (<quoted>)] => <quoted>)`.
452/// * [Conditionals](#conditionals) - `$(if <pattern> => <quoted>)`.
453/// * [Match Statements](#match-statements) - `$(match <expr> { [<pattern> => <quoted>,]* })`.
454///
455/// <br>
456///
457/// # Loops
458///
459/// To repeat a pattern you can use `$(for <bindings> in <expr> { <quoted> })`,
460/// where `<expr>` is an iterator.
461///
462/// It is also possible to use the more compact `$(for <bindings> in <expr> =>
463/// <quoted>)` (note the arrow).
464///
465/// `<quoted>` will be treated as a quoted expression, so anything which works
466/// during regular quoting will work here as well, with the addition that
467/// anything defined in `<bindings>` will be made available to the statement.
468///
469/// ```
470/// use genco::prelude::*;
471///
472/// let numbers = 3..=5;
473///
474/// let tokens: Tokens<()> = quote! {
475/// Your numbers are: $(for n in numbers => $n$[' '])
476/// };
477///
478/// assert_eq!("Your numbers are: 3 4 5", tokens.to_string()?);
479/// # Ok::<_, genco::fmt::Error>(())
480/// ```
481///
482/// <br>
483///
484/// # Joining Loops
485///
486/// You can add `join (<quoted>)` to the end of a repetition.
487///
488/// The expression specified in `join (<quoted>)` is added _between_ each
489/// element produced by the loop.
490///
491/// > *Note:* The argument to `join` is *whitespace sensitive*, so leading and
492/// > trailing is preserved. `join (,)` and `join (, )` would therefore produce
493/// > different results.
494///
495/// ```
496/// use genco::prelude::*;
497///
498/// let numbers = 3..=5;
499///
500/// let tokens: Tokens<()> = quote! {
501/// Your numbers are: $(for n in numbers join (, ) => $n).
502/// };
503///
504/// assert_eq!("Your numbers are: 3, 4, 5.", tokens.to_string()?);
505/// # Ok::<_, genco::fmt::Error>(())
506/// ```
507///
508/// <br>
509///
510/// # Conditionals
511///
512/// You can specify a conditional with `$(if <pattern> => <then>)` where
513/// `<pattern>` is an pattern or expression evaluating to a `bool`, and `<then>`
514/// is a quoted expressions.
515///
516/// It's also possible to specify a condition with an else branch, by using
517/// `$(if <pattern> { <then> } else { <else> })`. `<else>` is also a quoted
518/// expression.
519///
520/// ```
521/// use genco::prelude::*;
522///
523/// fn greeting(hello: bool, name: &str) -> Tokens<()> {
524/// quote!(Custom Greeting: $(if hello {
525/// Hello $name
526/// } else {
527/// Goodbye $name
528/// }))
529/// }
530///
531/// let tokens = greeting(true, "John");
532/// assert_eq!("Custom Greeting: Hello John", tokens.to_string()?);
533///
534/// let tokens = greeting(false, "John");
535/// assert_eq!("Custom Greeting: Goodbye John", tokens.to_string()?);
536/// # Ok::<_, genco::fmt::Error>(())
537/// ```
538///
539/// <br>
540///
541/// The `<else>` branch is optional, conditionals which do not have an else
542/// branch and evaluated to `false` won't produce any tokens:
543///
544/// ```
545/// use genco::prelude::*;
546///
547/// fn greeting(hello: bool, name: &str) -> Tokens<()> {
548/// quote!(Custom Greeting:$(if hello {
549/// $[' ']Hello $name
550/// }))
551/// }
552///
553/// let tokens = greeting(true, "John");
554/// assert_eq!("Custom Greeting: Hello John", tokens.to_string()?);
555///
556/// let tokens = greeting(false, "John");
557/// assert_eq!("Custom Greeting:", tokens.to_string()?);
558/// # Ok::<_, genco::fmt::Error>(())
559/// ```
560///
561/// <br>
562///
563/// # Match Statements
564///
565/// You can specify a match expression using `$(match <expr> { [<pattern> =>
566/// <quoted>,]* }`, where `<expr>` is an evaluated expression that is match
567/// against each subsequent `<pattern>`. If a pattern matches, the arm with the
568/// matching `<quoted>` block is evaluated.
569///
570/// ```
571/// use genco::prelude::*;
572///
573/// fn greeting(name: &str) -> Tokens<()> {
574/// quote!(Hello $(match name {
575/// "John" | "Jane" => $("Random Stranger"),
576/// other => $other,
577/// }))
578/// }
579///
580/// let tokens = greeting("John");
581/// assert_eq!("Hello Random Stranger", tokens.to_string()?);
582///
583/// let tokens = greeting("Mio");
584/// assert_eq!("Hello Mio", tokens.to_string()?);
585/// # Ok::<_, genco::fmt::Error>(())
586/// ```
587///
588/// If a match arm contains parenthesis (`=> (<quoted>)`), the expansion will be
589/// *whitespace sensitive*. Allowing leading and trailing whitespace to be
590/// preserved:
591///
592/// ```
593/// use genco::prelude::*;
594///
595/// fn greeting(name: &str) -> Tokens<()> {
596/// quote!(Hello$(match name {
597/// "John" | "Jane" => ( $("Random Stranger")),
598/// other => ( $other),
599/// }))
600/// }
601///
602/// let tokens = greeting("John");
603/// assert_eq!("Hello Random Stranger", tokens.to_string()?);
604///
605/// let tokens = greeting("Mio");
606/// assert_eq!("Hello Mio", tokens.to_string()?);
607/// # Ok::<_, genco::fmt::Error>(())
608/// ```
609///
610/// The following is an example with more complex matching:
611///
612/// ```
613/// use genco::prelude::*;
614///
615/// enum Greeting {
616/// Named(&'static str),
617/// Unknown,
618/// }
619///
620/// fn greeting(name: Greeting) -> Tokens<()> {
621/// quote!(Hello $(match name {
622/// Greeting::Named("John") | Greeting::Named("Jane") => $("Random Stranger"),
623/// Greeting::Named(other) => $other,
624/// Greeting::Unknown => $("Unknown Person"),
625/// }))
626/// }
627///
628/// let tokens = greeting(Greeting::Named("John"));
629/// assert_eq!("Hello Random Stranger", tokens.to_string()?);
630///
631/// let tokens = greeting(Greeting::Unknown);
632/// assert_eq!("Hello Unknown Person", tokens.to_string()?);
633///
634/// let tokens = greeting(Greeting::Named("Mio"));
635/// assert_eq!("Hello Mio", tokens.to_string()?);
636/// # Ok::<_, genco::fmt::Error>(())
637/// ```
638///
639/// <br>
640///
641/// # Variable assignment
642///
643/// You can use `$(let <binding> = <expr>)` to define variables with their value.
644/// This is useful within loops to compute values from iterator items.
645///
646/// ```
647/// use genco::prelude::*;
648///
649/// let names = ["A.B", "C.D"];
650///
651/// let tokens: Tokens<()> = quote! {
652/// $(for name in names =>
653/// $(let (first, second) = name.split_once('.').unwrap())
654/// $first and $second.
655/// )
656/// };
657/// assert_eq!("A and B.\nC and D.", tokens.to_string()?);
658/// # Ok::<_, genco::fmt::Error>(())
659/// ```
660///
661/// Variables can also be mutable:
662///
663/// ```
664/// use genco::prelude::*;
665/// let path = "A.B.C.D";
666///
667/// let tokens: Tokens<()> = quote! {
668/// $(let mut items = path.split('.'))
669/// $(if let Some(first) = items.next() =>
670/// First is $first
671/// )
672/// $(if let Some(second) = items.next() =>
673/// Second is $second
674/// )
675/// };
676///
677/// assert_eq!("First is A\nSecond is B", tokens.to_string()?);
678/// # Ok::<_, genco::fmt::Error>(())
679/// ```
680///
681/// <br>
682///
683/// # Scopes
684///
685/// You can use `$(ref <binding> { <expr> })` to gain access to the current
686/// token stream. This is an alternative to existing control flow operators if
687/// you want to run some custom code during evaluation which is otherwise not
688/// supported. This is called a *scope*.
689///
690/// For a more compact variant you can omit the braces with `$(ref <binding> =>
691/// <expr>)`.
692///
693/// ```
694/// use genco::prelude::*;
695///
696/// fn quote_greeting(surname: &str, lastname: Option<&str>) -> rust::Tokens {
697/// quote! {
698/// Hello $surname$(ref toks {
699/// if let Some(lastname) = lastname {
700/// toks.space();
701/// toks.append(lastname);
702/// }
703/// })
704/// }
705/// }
706///
707/// assert_eq!("Hello John", quote_greeting("John", None).to_string()?);
708/// assert_eq!("Hello John Doe", quote_greeting("John", Some("Doe")).to_string()?);
709/// # Ok::<_, genco::fmt::Error>(())
710/// ```
711///
712/// <br>
713///
714/// ## Whitespace Detection
715///
716/// The [`quote!`] macro has the following rules for dealing with indentation and
717/// spacing.
718///
719/// **Spaces** — Two tokens that are separated are spaced. Regardless of how
720/// many spaces there are between them. This can be controlled manually by
721/// inserting the [`$[' ']`][escape] escape sequence in the token stream.
722///
723/// ```
724/// use genco::prelude::*;
725///
726/// let tokens: rust::Tokens = quote! {
727/// fn test() {
728/// println!("Hello... ");
729///
730/// println!("World!");
731/// }
732/// };
733///
734/// assert_eq!(
735/// vec![
736/// "fn test() {",
737/// " println!(\"Hello... \");",
738/// "",
739/// " println!(\"World!\");",
740/// "}",
741/// ],
742/// tokens.to_file_vec()?,
743/// );
744/// # Ok::<_, genco::fmt::Error>(())
745/// ```
746///
747/// <br>
748///
749/// **Line breaking** — Line breaks are detected by leaving two empty lines
750/// between two tokens. This can be controlled manually by inserting the
751/// [`$['\n']`][escape] escape in the token stream.
752///
753/// ```
754/// use genco::prelude::*;
755///
756/// let tokens: rust::Tokens = quote! {
757/// fn test() {
758/// println!("Hello... ");
759///
760///
761///
762/// println!("World!");
763/// }
764/// };
765///
766/// assert_eq!(
767/// vec![
768/// "fn test() {",
769/// " println!(\"Hello... \");",
770/// "",
771/// " println!(\"World!\");",
772/// "}",
773/// ],
774/// tokens.to_file_vec()?,
775/// );
776/// # Ok::<_, genco::fmt::Error>(())
777/// ```
778///
779/// <br>
780///
781/// **Indentation** — Indentation is determined on a row-by-row basis. If a
782/// column is further in than the one on the preceeding row, it is indented *one
783/// level* deeper.
784///
785/// If a column starts shallower than a preceeding, non-whitespace only row, it
786/// will be matched against previously known indentation levels. Failure to
787/// match a previously known level is an error.
788///
789/// All indentations inserted during the macro will be unrolled at the end of
790/// it. So any trailing indentations will be matched by unindentations.
791///
792/// ```
793/// use genco::prelude::*;
794///
795/// let tokens: rust::Tokens = quote! {
796/// fn test() {
797/// println!("Hello... ");
798///
799/// println!("World!");
800/// }
801/// };
802///
803/// assert_eq!(
804/// vec![
805/// "fn test() {",
806/// " println!(\"Hello... \");",
807/// "",
808/// " println!(\"World!\");",
809/// "}",
810/// ],
811/// tokens.to_file_vec()?,
812/// );
813/// # Ok::<_, genco::fmt::Error>(())
814/// ```
815///
816/// Example showcasing an indentation mismatch:
817///
818/// ```,compile_fail
819/// use genco::prelude::*;
820///
821/// let tokens: rust::Tokens = quote! {
822/// fn test() {
823/// println!("Hello... ");
824///
825/// println!("World!");
826/// }
827/// };
828/// ```
829///
830/// ```text
831/// ---- src\lib.rs - (line 150) stdout ----
832/// error: expected 4 less spaces of indentation
833/// --> src\lib.rs:157:9
834/// |
835/// 10 | println!("World!");
836/// | ^^^^^^^
837/// ```
838///
839/// [escape]: #escape-sequences
840pub use genco_macros::quote;
841
842/// Convenience macro for constructing a [`FormatInto`] implementation in-place.
843///
844/// Constructing [`FormatInto`] implementation instead of short lived [token
845/// streams] can be more beneficial for memory use and performance.
846///
847/// [`FormatInto`]: crate::tokens::FormatInto
848/// [token streams]: Tokens
849///
850/// # Comparison
851///
852/// In the below example, `f1` and `f2` are equivalent. In here [quote_fn!]
853/// simply makes it easier to build.
854///
855/// ```
856/// use genco::prelude::*;
857/// use genco::tokens::from_fn;
858///
859/// let f1 = from_fn(move |t| {
860/// quote_in!{ *t =>
861/// println!("Hello World");
862/// }
863/// });
864///
865/// let f2 = quote_fn!{
866/// println!("Hello World");
867/// };
868///
869/// let tokens: rust::Tokens = quote!{
870/// $f1
871/// $f2
872/// };
873///
874/// assert_eq!{
875/// vec![
876/// "println!(\"Hello World\");",
877/// "println!(\"Hello World\");",
878/// ],
879/// tokens.to_file_vec()?,
880/// };
881/// # Ok::<_, genco::fmt::Error>(())
882/// ```
883///
884/// # Examples which borrow
885///
886/// ```
887/// use genco::prelude::*;
888///
889/// fn greeting(name: &str) -> impl FormatInto<Rust> + '_ {
890/// quote_fn! {
891/// println!($[str](Hello $[const](name)))
892/// }
893/// }
894///
895/// fn advanced_greeting<'a>(first: &'a str, last: &'a str) -> impl FormatInto<Rust> + 'a {
896/// quote_fn! {
897/// println!($[str](Hello $[const](first) $[const](last)))
898/// }
899/// }
900///
901/// let tokens = quote! {
902/// $(greeting("Mio"));
903/// $(advanced_greeting("Jane", "Doe"));
904/// };
905///
906/// assert_eq!{
907/// vec![
908/// "println!(\"Hello Mio\");",
909/// "println!(\"Hello Jane Doe\");",
910/// ],
911/// tokens.to_file_vec()?
912/// };
913/// # Ok::<_, genco::fmt::Error>(())
914/// ```
915pub use genco_macros::quote_fn;
916
917/// Behaves the same as [`quote!`] while quoting into an existing token stream
918/// with `<target> => <quoted>`.
919///
920/// This macro takes a destination stream followed by an `=>` and the tokens to
921/// extend that stream with.
922///
923/// Note that it must be possible to borrow `<target>` mutably, so a reference
924/// like `&mut rust::Tokens` will have to be dereferenced when used with this
925/// macro.
926///
927/// ```
928/// # use genco::prelude::*;
929///
930/// # fn generate() -> rust::Tokens {
931/// let mut tokens = rust::Tokens::new();
932/// quote_in!(tokens => hello world);
933/// # tokens
934/// # }
935///
936/// fn generate_into(tokens: &mut rust::Tokens) {
937/// quote_in! { *tokens =>
938/// hello...
939/// world!
940/// };
941/// }
942/// ```
943///
944/// # Example
945///
946/// ```
947/// use genco::prelude::*;
948///
949/// let mut tokens = rust::Tokens::new();
950///
951/// quote_in! { tokens =>
952/// fn foo() -> u32 {
953/// 42
954/// }
955/// }
956/// ```
957///
958/// # Use with scopes
959///
960/// [`quote_in!`] can be used inside of a [`quote!`] through [a scope].
961///
962/// ```
963/// use genco::prelude::*;
964///
965/// let tokens: rust::Tokens = quote! {
966/// fn foo(v: bool) -> u32 {
967/// $(ref out {
968/// quote_in! { *out =>
969/// if v {
970/// 1
971/// } else {
972/// 0
973/// }
974/// }
975/// })
976/// }
977/// };
978/// ```
979///
980/// [a scope]: quote#scopes
981pub use genco_macros::quote_in;
982
983#[macro_use]
984mod macros;
985pub mod fmt;
986pub mod lang;
987pub mod prelude;
988pub mod tokens;
989#[doc(inline)]
990pub use self::tokens::Tokens;
991
992/// Private module used for macros.
993#[doc(hidden)]
994pub mod __priv {
995 use alloc::boxed::Box;
996
997 use crate::lang::Lang;
998 use crate::tokens::{from_fn, FormatInto};
999 use crate::tokens::{Item, ItemStr};
1000
1001 #[inline]
1002 pub const fn static_<L>(string: &'static str) -> Item<L>
1003 where
1004 L: Lang,
1005 {
1006 Item::static_(string)
1007 }
1008
1009 #[inline]
1010 pub const fn literal<L>(string: ItemStr) -> Item<L>
1011 where
1012 L: Lang,
1013 {
1014 Item::literal(string)
1015 }
1016
1017 #[inline]
1018 pub const fn indentation<L>(level: i16) -> Item<L>
1019 where
1020 L: Lang,
1021 {
1022 Item::indentation(level)
1023 }
1024
1025 #[inline]
1026 pub const fn push<L>() -> Item<L>
1027 where
1028 L: Lang,
1029 {
1030 Item::push()
1031 }
1032
1033 #[inline]
1034 pub const fn line<L>() -> Item<L>
1035 where
1036 L: Lang,
1037 {
1038 Item::line()
1039 }
1040
1041 #[inline]
1042 pub const fn space<L>() -> Item<L>
1043 where
1044 L: Lang,
1045 {
1046 Item::space()
1047 }
1048
1049 #[inline]
1050 pub const fn open_quote<L>(is_interpolation: bool) -> Item<L>
1051 where
1052 L: Lang,
1053 {
1054 Item::open_quote(is_interpolation)
1055 }
1056
1057 #[inline]
1058 pub const fn close_quote<L>() -> Item<L>
1059 where
1060 L: Lang,
1061 {
1062 Item::close_quote()
1063 }
1064
1065 #[inline]
1066 pub const fn open_eval<L>() -> Item<L>
1067 where
1068 L: Lang,
1069 {
1070 Item::open_eval()
1071 }
1072
1073 #[inline]
1074 pub const fn close_eval<L>() -> Item<L>
1075 where
1076 L: Lang,
1077 {
1078 Item::close_eval()
1079 }
1080
1081 /// Add a language item directly.
1082 ///
1083 /// This must only be used by the [`impl_lang!`] macro.
1084 ///
1085 /// [`impl_lang!`]: crate::impl_lang!
1086 #[doc(hidden)]
1087 #[inline]
1088 pub fn item<L>(item: L::Item) -> impl FormatInto<L>
1089 where
1090 L: Lang,
1091 {
1092 from_fn(|t| {
1093 t.lang_item(Box::new(item));
1094 })
1095 }
1096
1097 /// Register a language item directly.
1098 ///
1099 /// This must only be used by the [`impl_lang!`] macro.
1100 ///
1101 /// [`impl_lang!`]: crate::impl_lang!
1102 #[doc(hidden)]
1103 #[inline]
1104 pub fn register<L>(item: L::Item) -> impl FormatInto<L>
1105 where
1106 L: Lang,
1107 {
1108 from_fn(|t| {
1109 t.lang_item_register(Box::new(item));
1110 })
1111 }
1112}