    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74

    use proc_macro2::TokenStream;
    use quote::ToTokens;
    use syn::token;

    pub enum Fragment {
        /// Tokens that can be used as an expression.
        Expr(TokenStream),
        /// Tokens that can be used inside a block. The surrounding curly braces are
        /// not part of these tokens.
        Block(TokenStream),
    }

    macro_rules! quote_expr {
        ($($tt:tt)*) => {
            $crate::fragment::Fragment::Expr(quote!($($tt)*))
        }
    }

    macro_rules! quote_block {
        ($($tt:tt)*) => {
            $crate::fragment::Fragment::Block(quote!($($tt)*))
        }
    }

    /// Interpolate a fragment in place of an expression. This involves surrounding
    /// Block fragments in curly braces.
    pub struct Expr(pub Fragment);
    impl ToTokens for Expr {
        fn to_tokens(&self, out: &mut TokenStream) {
            match &self.0 {
                Fragment::Expr(expr) => expr.to_tokens(out),
                Fragment::Block(block) => {
                    token::Brace::default().surround(out, |out| block.to_tokens(out));
                }
            }
        }
    }

    /// Interpolate a fragment as the statements of a block.
    pub struct Stmts(pub Fragment);
    impl ToTokens for Stmts {
        fn to_tokens(&self, out: &mut TokenStream) {
            match &self.0 {
                Fragment::Expr(expr) => expr.to_tokens(out),
                Fragment::Block(block) => block.to_tokens(out),
            }
        }
    }

    /// Interpolate a fragment as the value part of a `match` expression. This
    /// involves putting a comma after expressions and curly braces around blocks.
    pub struct Match(pub Fragment);
    impl ToTokens for Match {
        fn to_tokens(&self, out: &mut TokenStream) {
            match &self.0 {
                Fragment::Expr(expr) => {
                    expr.to_tokens(out);
                    <Token![,]>::default().to_tokens(out);
                }
                Fragment::Block(block) => {
                    token::Brace::default().surround(out, |out| block.to_tokens(out));
                }
            }
        }
    }

    impl AsRef<TokenStream> for Fragment {
        fn as_ref(&self) -> &TokenStream {
            match self {
                Fragment::Expr(expr) => expr,
                Fragment::Block(block) => block,
            }
        }
    }