potent_quotables/
lib.rs

1#![doc = include_str!("../README.md")]
2
3/// Quotes its argument tokens, yielding a [TokenStream].
4///
5/// ```
6/// # use potent_quotables::pq;
7/// let tokens = pq! { x * (y + 1) };
8/// assert_eq!(tokens.to_string(), "x * (y + 1)");
9/// ```
10///
11/// # Directives
12///
13/// Sequences of tokens starting with `#` are known as *directives* and activate special behaviors
14/// as described in the following sections.
15///
16/// As an exception, the sequences `#[` and `#!` are *not* interpreted as directives and are instead
17/// quoted just like any other tokens, allowing you to quote Rust attributes.
18///
19/// ```
20/// # use potent_quotables::pq;
21/// let tokens = pq! { #![foo] #[bar] };
22/// assert_eq!(tokens.to_string(), "#! [foo] # [bar]");
23/// ```
24///
25/// ## Interpolation
26///
27/// - **`#`<var>ident</var>**
28/// - **`#(`<var>expr</var>`)`**
29///
30/// This directive evaluates <var>ident</var> or <var>expr</var>. The resulting value must implement
31/// the [ToTokens] trait, which is used to convert the value into a sequence of tokens that will be
32/// included in the [TokenStream].
33///
34/// ```
35/// # use potent_quotables::pq;
36/// let greeting = "hello";
37/// let tokens = pq! { #greeting #(greeting.len()) };
38/// assert_eq!(tokens.to_string(), "\"hello\" 5usize");
39/// ```
40///
41/// Expressions more complex than a single identifier *must* be enclosed in parentheses.
42///
43/// ## Statement directives
44///
45/// - **`#do` <var>stmt</var>**
46/// - **`#` <var>stmt</var>**, where `stmt` starts with one of the following keywords:
47///
48///   `break` `continue` `enum` `extern` `fn` `impl` `let` `mod` `pub` `return` `static` `struct`
49///   `trait` `type` `use`
50///
51/// The main use cases for statement directives are declaring local variables with `let` and
52/// evaluating expressions whose results should not (or cannot) be [interpolated](#interpolation).
53///
54/// ```
55/// # use potent_quotables::pq;
56/// let tokens = pq! {
57///     #let mut x = 3;
58///     #x
59///     #do x += 2; // compound-assignment expression has type `()`, which cannot be interpolated
60///     #x
61/// };
62/// assert_eq!(tokens.to_string(), "3i32 5i32");
63/// ```
64///
65/// Another important use case allows using `break` or `continue` to manipulate control flow in a
66/// loop—either a loop surrounding the `pq!` invocation or a loop introduced by a [control-flow
67/// directive](#control-flow-directives).
68///
69/// ```
70/// # use potent_quotables::pq;
71/// loop {
72///     pq! { #break; };
73///     unreachable!()
74/// }
75/// ```
76///
77/// Statements usually end in a semicolon, but for some kinds of statements, the trailing semicolon
78/// is optional. When an optional trailing semicolon is present in such a statement, it is always
79/// treated as part of the directive, rather than as an unrelated token to be quoted. If you need to
80/// quote a semicolon immediately after a statement directive whose semicolon is optional, you will
81/// need to include the optional semicolon *and* the semicolon to be quoted.
82///
83/// ```
84/// # use potent_quotables::pq;
85/// let tokens = pq! {
86///     #do {}; // optional trailing semicolon
87///     ; // semicolon to be quoted
88/// };
89/// assert_eq!(tokens.to_string(), ";");
90/// ```
91///
92/// ## Control-flow directives
93///
94/// These directives behave analogously to their corresponding Rust control-flow expressions, except
95/// that their body blocks are treated as sequences of tokens to be quoted instead of code to be
96/// executed.
97///
98/// Labeled loops are not supported.
99///
100/// - **`#if` <var>cond</var> `{` <var>token</var><sup>\*</sup> `}` (`else if` <var>cond</var> `{`
101///   <var>token</var><sup>\*</sup> `}`)<sup>*</sup> (`else` `{` <var>token</var><sup>\*</sup>
102///   `}`)<sup>?</sup>**
103///
104///   ```
105///   # use potent_quotables::pq;
106///   let greeting = "hello";
107///   let tokens = pq! {
108///       #if greeting.len() > 3 {
109///           goodbye
110///       } else if greeting.len() > 1 {
111///           bye
112///       } else {
113///           nope
114///       }
115///   };
116///   assert_eq!(tokens.to_string(), "goodbye");
117///   ```
118///
119///   Note that an `else` keyword immediately following the "then" block is always treated as part
120///   of the directive, rather than as an unrelated token to be quoted. If you need to quote the
121///   `else` keyword immediately after a `#if` directive, you can use a no-op [statement
122///   directive](#statement-directives) to separate them.
123///
124///   ```
125///   # use potent_quotables::pq;
126///   let tokens = pq! {
127///       #if true { foo }
128///       #do {} // no-op statement to separate the `else` from the `#if`
129///       else
130///   };
131///   assert_eq!(tokens.to_string(), "foo else");
132///   ```
133///
134/// - **`#while` <var>cond</var> `{` <var>token</var><sup>\*</sup> `}`**
135///
136///   ```
137///   # use potent_quotables::pq;
138///   let mut iter = ["fee", "fi", "fo", "fum"].into_iter();
139///   let tokens = pq! {
140///       #while let Some(word) = iter.next() {
141///           #word;
142///       }
143///   };
144///   assert_eq!(tokens.to_string(), "\"fee\" ; \"fi\" ; \"fo\" ; \"fum\" ;");
145///   ```
146///
147/// - **`#for` <var>pat</var> `in` <var>expr</var> `{` <var>token</var><sup>\*</sup> `}`**
148///
149///   ```
150///   # use potent_quotables::pq;
151///   let tokens = pq! {
152///       #for i in 0..5 {
153///           #i
154///       }
155///   };
156///   assert_eq!(tokens.to_string(), "0i32 1i32 2i32 3i32 4i32");
157///   ```
158///
159/// - **`#loop` `{` <var>token</var><sup>\*</sup> `}`**
160///
161///   ```
162///   # use potent_quotables::pq;
163///   let tokens = pq! {
164///       #loop {
165///           *
166///           #break;
167///       }
168///   };
169///   assert_eq!(tokens.to_string(), "*");
170///   ```
171///
172/// - **`#match` <var>expr</var> `{` ( <var>pat</var> `=>` `{` <var>token</var><sup>\*</sup> `}`
173///   `,`<sup>?</sup> )<sup>*</sup> `}`**
174///
175///   The right-hand side of each arm must be a braced group, which is treated as a sequence of
176///   tokens to be quoted.
177///
178///   ```
179///   # use potent_quotables::pq;
180///   let result = Some(1);
181///   let tokens = pq! {
182///       #match result {
183///           Some(x) => { #x }
184///           None => { nope }
185///       }
186///   };
187///   assert_eq!(tokens.to_string(), "1i32");
188///   ```
189///
190/// ## Formatted doc comments
191///
192/// - **`#//!` <var>text</var>**
193/// - **`#///` <var>text</var>**
194///
195/// This directive produces a doc comment, with <var>text</var> treated as a format string à la
196/// [std::fmt].
197///
198/// ```
199/// # use potent_quotables::pq;
200/// let greeting = "hello";
201/// let recipient = Some(1);
202/// let tokens = pq! {
203///     #//! {greeting} world
204///     #/// is {recipient:?} out there?
205/// };
206/// assert_eq!(
207///     tokens.to_string(),
208///     "#![doc = \" hello world\"] #[doc = \" is Some(1) out there?\"]",
209/// );
210/// ```
211///
212/// All named parameters in <var>text</var> must refer to variables in scope. Positional parameters
213/// are not allowed because the directive's syntax does not provide a way to pass additional
214/// arguments to the formatter.
215///
216/// # Scoping
217///
218/// The body of a `pq!` invocation introduces its own lexical scope. Additionally, each delimited
219/// group introduces its own lexical scope.
220///
221/// ```
222/// # use potent_quotables::pq;
223/// let x = "outermost";
224/// let tokens = pq! {
225///     #let x = "outside";
226///     {
227///         #let x = "braced";
228///         [
229///             #let x = "bracketed";
230///             (
231///                 #let x = "parenthesized";
232///                 #x
233///             )
234///             #x
235///         ]
236///         #x
237///     }
238///     #x
239/// };
240/// assert_eq!(x, "outermost");
241/// assert_eq!(
242///     tokens.to_string(),
243///     "{ [(\"parenthesized\") \"bracketed\"] \"braced\" } \"outside\"",
244/// );
245/// ```
246#[macro_export]
247macro_rules! pq {
248    ($($tts:tt)*) => {
249        $crate::private::pq!($($tts)*)
250    };
251}
252
253#[doc(no_inline)]
254pub use proc_macro2::TokenStream;
255#[doc(no_inline)]
256pub use quote::ToTokens;
257
258#[doc(hidden)]
259pub mod private {
260    pub use std::format;
261
262    pub use potent_quotables_core::*;
263    pub use potent_quotables_proc_macro::pq;
264}