mod args;
mod regex_code;
use {
crate::{args::*, regex_code::*},
proc_macro::TokenStream,
quote::quote,
std::convert::TryFrom,
syn::{parse_macro_input, Expr, ExprClosure},
};
fn process<T, F>(input: TokenStream, f: F) -> TokenStream
where
T: Into<TokenStream>,
F: Fn(RegexCode) -> T,
{
match RegexCode::try_from(input) {
Ok(r) => f(r).into(),
Err(e) => e.to_compile_error().into(),
}
}
fn process_with_value<T, F>(input: TokenStream, f: F) -> TokenStream
where
T: Into<TokenStream>,
F: Fn(RegexCode, Expr) -> T,
{
let parsed = parse_macro_input!(input as RexValArgs);
match RegexCode::try_from(parsed.regex_str) {
Ok(r) => f(r, parsed.value).into(),
Err(e) => e.to_compile_error().into(),
}
}
fn process_with_value_fun<T, F>(input: TokenStream, f: F) -> TokenStream
where
T: Into<TokenStream>,
F: Fn(RegexCode, Expr, ExprClosure) -> T,
{
let parsed = parse_macro_input!(input as RexValFunArgs);
match RegexCode::try_from(parsed.regex_str) {
Ok(r) => f(r, parsed.value, parsed.fun).into(),
Err(e) => e.to_compile_error().into(),
}
}
#[proc_macro]
pub fn regex(input: TokenStream) -> TokenStream {
process(input, |regex_code| regex_code.lazy_static())
}
#[proc_macro]
pub fn lazy_regex(input: TokenStream) -> TokenStream {
process(input, |regex_code| regex_code.build)
}
#[proc_macro]
pub fn regex_is_match(input: TokenStream) -> TokenStream {
process_with_value(input, |regex_code, value| {
let statick = regex_code.statick();
quote! {{
#statick;
RE.is_match(#value)
}}
})
}
#[proc_macro]
pub fn regex_find(input: TokenStream) -> TokenStream {
process_with_value(input, |regex_code, value| {
let statick = regex_code.statick();
let as_method = match regex_code.regex {
RegexInstance::Regex(..) => quote!(as_str),
RegexInstance::Bytes(..) => quote!(as_bytes),
};
quote! {{
#statick;
RE.find(#value).map(|mat| mat. #as_method ())
}}
})
}
#[proc_macro]
pub fn regex_captures(input: TokenStream) -> TokenStream {
process_with_value(input, |regex_code, value| {
let statick = regex_code.statick();
let n = regex_code.captures_len();
let groups = (0..n).map(|i| {
quote! {
caps.get(#i).map_or("", |c| c.as_str())
}
});
quote! {{
#statick;
RE.captures(#value)
.map(|caps| (
#(#groups),*
))
}}
})
}
fn replacen(input: TokenStream, limit: usize) -> TokenStream {
process_with_value_fun(input, |regex_code, value, fun| {
let statick = regex_code.statick();
let n = regex_code.captures_len();
let groups = (0..n).map(|i| {
quote! {
caps.get(#i).map_or("", |c| c.as_str())
}
});
quote! {{
#statick;
RE.replacen(
#value,
#limit,
|caps: &lazy_regex::Captures<'_>| {
let fun = #fun;
fun(
#(#groups),*
)
})
}}
})
}
#[proc_macro]
pub fn regex_replace(input: TokenStream) -> TokenStream {
replacen(input, 1)
}
#[proc_macro]
pub fn regex_replace_all(input: TokenStream) -> TokenStream {
replacen(input, 0)
}