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
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#![allow(clippy::or_fun_call)]
#![allow(clippy::useless_conversion)]
#![allow(irrefutable_let_patterns)]

extern crate proc_macro;

#[cfg(not(feature = "proc_macro2_"))]
use proc_macro as used_proc_macro;

#[cfg(feature = "proc_macro2_")]
use proc_macro2 as used_proc_macro;

use std::iter;

#[allow(unused_imports)]
use used_proc_macro::{
    Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree,
};

mod parsing;

mod utils;

#[doc(hidden)]
#[proc_macro]
pub fn __priv_bstr_start(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
    bstr_pattern(input_tokens.into(), StrAt::Start).into()
}

#[doc(hidden)]
#[proc_macro]
pub fn __priv_bstr_end(input_tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
    bstr_pattern(input_tokens.into(), StrAt::End).into()
}

fn bstr_pattern(input_tokens: TokenStream, str_at: StrAt) -> TokenStream {
    use crate::utils::punct_token;

    let parsed = parsing::parse_inputs(input_tokens);

    match parsed {
        Ok(Inputs { rem_ident, strings }) => {
            let mut out = TokenStream::new();

            for (i, patt) in strings.iter().enumerate() {
                let span = patt.span(&rem_ident);

                if i != 0 {
                    out.extend(punct_token('|', span));
                }
                let tt = crate::utils::bracket(Span::call_site(), |out| match str_at {
                    StrAt::Start => {
                        output_patt(patt, out);
                        output_remainder_pat(&rem_ident, out);
                    }
                    StrAt::End => {
                        output_remainder_pat(&rem_ident, out);
                        out.extend(punct_token(',', span));
                        output_patt(patt, out);
                    }
                });

                out.extend(iter::once(tt))
            }

            out
        }
        Err(e) => e.to_compile_error(),
    }
}

fn output_patt(patt: &Pattern, out: &mut TokenStream) {
    use crate::utils::punct_token;

    match patt {
        Pattern::String { string, span } => {
            for b in string.bytes() {
                let mut lit = Literal::u8_unsuffixed(b);
                lit.set_span(*span);
                out.extend(iter::once(TokenTree::from(lit)));
                out.extend(punct_token(',', *span));
            }
        }
    }
}

fn output_remainder_pat(patt: &Ident, out: &mut TokenStream) {
    use crate::utils::{punct_joint_token2, punct_token};

    out.extend(iter::once(TokenTree::from(patt.clone())));

    out.extend(punct_token('@', patt.span()));

    out.extend(punct_joint_token2('.', '.', patt.span()));
}

struct Inputs {
    rem_ident: Ident,
    strings: Vec<Pattern>,
}

enum Pattern {
    String { string: String, span: Span },
}

enum StrAt {
    Start,
    End,
}

impl Pattern {
    fn span(&self, _rem_ident: &Ident) -> Span {
        match self {
            Pattern::String { span, .. } => *span,
        }
    }
}