mod args;
mod regex_code;
use {
crate::{args::*, regex_code::*},
proc_macro::TokenStream,
quote::quote,
syn::{parse_macro_input, Expr},
};
fn process<T, F>(input: TokenStream, as_bytes: bool, f: F) -> TokenStream
where
T: Into<TokenStream>,
F: Fn(RegexCode) -> T,
{
match RegexCode::from_token_stream(input, as_bytes) {
Ok(r) => f(r).into(),
Err(e) => e.to_compile_error().into(),
}
}
fn process_with_value<T, F>(input: TokenStream, as_bytes: bool, f: F) -> TokenStream
where
T: Into<TokenStream>,
F: Fn(RegexCode, Expr) -> T,
{
let parsed = parse_macro_input!(input as RexValArgs);
match RegexCode::from_lit_str(parsed.regex_str, as_bytes) {
Ok(r) => f(r, parsed.value).into(),
Err(e) => e.to_compile_error().into(),
}
}
#[proc_macro]
pub fn regex(input: TokenStream) -> TokenStream {
process(input, false, |regex_code| regex_code.lazy_static())
}
#[proc_macro]
pub fn bytes_regex(input: TokenStream) -> TokenStream {
process(input, true, |regex_code| regex_code.lazy_static())
}
#[proc_macro]
pub fn lazy_regex(input: TokenStream) -> TokenStream {
process(input, false, |regex_code| regex_code.build)
}
#[proc_macro]
pub fn bytes_lazy_regex(input: TokenStream) -> TokenStream {
process(input, true, |regex_code| regex_code.build)
}
#[proc_macro]
pub fn regex_is_match(input: TokenStream) -> TokenStream {
process_with_value(input, false, |regex_code, value| {
let statick = regex_code.statick();
quote! {{
#statick;
RE.is_match(#value)
}}
})
}
#[proc_macro]
pub fn bytes_regex_is_match(input: TokenStream) -> TokenStream {
process_with_value(input, true, |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, false, |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 bytes_regex_find(input: TokenStream) -> TokenStream {
process_with_value(input, true, |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, false, |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),*
))
}}
})
}
#[proc_macro]
pub fn bytes_regex_captures(input: TokenStream) -> TokenStream {
process_with_value(input, true, |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(&b""[..], |c| c.as_bytes())
}
});
quote! {{
#statick;
RE.captures(#value)
.map(|caps| (
#(#groups),*
))
}}
})
}
fn replacen(input: TokenStream, limit: usize) -> TokenStream {
let parsed = parse_macro_input!(input as ReplaceArgs);
let ReplaceArgs { regex_str, value, replacer } = parsed;
let regex_code = match RegexCode::from_lit_str(regex_str, false) {
Ok(r) => r,
Err(e) => {
return e.to_compile_error().into();
}
};
let statick = regex_code.statick();
let stream = match replacer {
MaybeFun::Fun(fun) => {
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 mut fun = #fun;
fun(
#(#groups),*
)
})
}}
}
MaybeFun::Expr(expr) => {
quote! {{
#statick;
RE.replacen(#value, #limit, #expr)
}}
}
};
stream.into()
}
fn bytes_replacen(input: TokenStream, limit: usize) -> TokenStream {
let parsed = parse_macro_input!(input as ReplaceArgs);
let ReplaceArgs { regex_str, value, replacer } = parsed;
let regex_code = match RegexCode::from_lit_str(regex_str, true) {
Ok(r) => r,
Err(e) => {
return e.to_compile_error().into();
}
};
let statick = regex_code.statick();
let stream = match replacer {
MaybeFun::Fun(fun) => {
let n = regex_code.captures_len();
let groups = (0..n).map(|i| {
quote! {
caps.get(#i).map_or(&b""[..], |c| c.as_bytes())
}
});
quote! {{
#statick;
RE.replacen(
#value,
#limit,
|caps: &lazy_regex::regex::bytes::Captures<'_>| {
let mut fun = #fun;
fun(
#(#groups),*
)
})
}}
}
MaybeFun::Expr(expr) => {
quote! {{
#statick;
RE.replacen(#value, #limit, #expr)
}}
}
};
stream.into()
}
#[proc_macro]
pub fn regex_replace(input: TokenStream) -> TokenStream {
replacen(input, 1)
}
#[proc_macro]
pub fn bytes_regex_replace(input: TokenStream) -> TokenStream {
bytes_replacen(input, 1)
}
#[proc_macro]
pub fn regex_replace_all(input: TokenStream) -> TokenStream {
replacen(input, 0)
}
#[proc_macro]
pub fn bytes_regex_replace_all(input: TokenStream) -> TokenStream {
bytes_replacen(input, 0)
}