1 use proc_macro2::TokenStream;
2 use quote::ToTokens;
3 use syn::token;
4 
5 pub enum Fragment {
6     /// Tokens that can be used as an expression.
7     Expr(TokenStream),
8     /// Tokens that can be used inside a block. The surrounding curly braces are
9     /// not part of these tokens.
10     Block(TokenStream),
11 }
12 
13 macro_rules! quote_expr {
14     ($($tt:tt)*) => {
15         $crate::fragment::Fragment::Expr(quote!($($tt)*))
16     }
17 }
18 
19 macro_rules! quote_block {
20     ($($tt:tt)*) => {
21         $crate::fragment::Fragment::Block(quote!($($tt)*))
22     }
23 }
24 
25 /// Interpolate a fragment in place of an expression. This involves surrounding
26 /// Block fragments in curly braces.
27 pub struct Expr(pub Fragment);
28 impl ToTokens for Expr {
to_tokens(&self, out: &mut TokenStream)29     fn to_tokens(&self, out: &mut TokenStream) {
30         match &self.0 {
31             Fragment::Expr(expr) => expr.to_tokens(out),
32             Fragment::Block(block) => {
33                 token::Brace::default().surround(out, |out| block.to_tokens(out));
34             }
35         }
36     }
37 }
38 
39 /// Interpolate a fragment as the statements of a block.
40 pub struct Stmts(pub Fragment);
41 impl ToTokens for Stmts {
to_tokens(&self, out: &mut TokenStream)42     fn to_tokens(&self, out: &mut TokenStream) {
43         match &self.0 {
44             Fragment::Expr(expr) => expr.to_tokens(out),
45             Fragment::Block(block) => block.to_tokens(out),
46         }
47     }
48 }
49 
50 /// Interpolate a fragment as the value part of a `match` expression. This
51 /// involves putting a comma after expressions and curly braces around blocks.
52 pub struct Match(pub Fragment);
53 impl ToTokens for Match {
to_tokens(&self, out: &mut TokenStream)54     fn to_tokens(&self, out: &mut TokenStream) {
55         match &self.0 {
56             Fragment::Expr(expr) => {
57                 expr.to_tokens(out);
58                 <Token![,]>::default().to_tokens(out);
59             }
60             Fragment::Block(block) => {
61                 token::Brace::default().surround(out, |out| block.to_tokens(out));
62             }
63         }
64     }
65 }
66 
67 impl AsRef<TokenStream> for Fragment {
as_ref(&self) -> &TokenStream68     fn as_ref(&self) -> &TokenStream {
69         match self {
70             Fragment::Expr(expr) => expr,
71             Fragment::Block(block) => block,
72         }
73     }
74 }
75