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)
}
#[proc_macro]
pub fn regex_if(input: TokenStream) -> TokenStream {
let RexIfArgs {
regex_str,
value,
then,
} = parse_macro_input!(input as RexIfArgs);
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 assigns = regex_code.named_groups().into_iter().map(|(idx, name)| {
let var_name = syn::Ident::new(name, proc_macro2::Span::call_site());
quote! {
let #var_name: &str = caps.get(#idx).map_or("", |c| c.as_str());
}
});
quote! {{
#statick;
match RE.captures(#value) {
Some(caps) => {
#(#assigns);*
Some(#then)
}
None => None,
}
}}.into()
}
#[proc_macro]
pub fn bytes_regex_if(input: TokenStream) -> TokenStream {
let RexIfArgs {
regex_str,
value,
then,
} = parse_macro_input!(input as RexIfArgs);
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 assigns = regex_code.named_groups().into_iter().map(|(idx, name)| {
let var_name = syn::Ident::new(name, proc_macro2::Span::call_site());
quote! {
let #var_name: &[u8] = caps.get(#idx).map_or(&b""[..], |c| c.as_bytes());
}
});
quote! {{
#statick;
match RE.captures(#value) {
Some(caps) => {
#(#assigns);*
Some(#then)
}
None => None,
}
}}.into()
}
#[proc_macro]
pub fn regex_switch(input: TokenStream) -> TokenStream {
let RexSwitchArgs {
value,
arms,
} = parse_macro_input!(input as RexSwitchArgs);
let mut q_arms = Vec::new();
for RexSwitchArmArgs { regex_str, then } in arms.into_iter() {
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 assigns = regex_code.named_groups().into_iter().map(|(idx, name)| {
let var_name = syn::Ident::new(name, proc_macro2::Span::call_site());
quote! {
let #var_name: &str = caps.get(#idx).map_or("", |c| c.as_str());
}
});
q_arms.push(
quote! {{
#statick;
if let Some(caps) = RE.captures(#value) {
#(#assigns);*
let output = Some(#then);
break 'switch output;
}
}}
);
}
quote! {{
'switch: {
#(#q_arms)*
None
}
}}.into()
}
#[proc_macro]
pub fn bytes_regex_switch(input: TokenStream) -> TokenStream {
let RexSwitchArgs {
value,
arms,
} = parse_macro_input!(input as RexSwitchArgs);
let mut q_arms = Vec::new();
for RexSwitchArmArgs { regex_str, then } in arms.into_iter() {
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 assigns = regex_code.named_groups().into_iter().map(|(idx, name)| {
let var_name = syn::Ident::new(name, proc_macro2::Span::call_site());
quote! {
let #var_name: &[u8] = caps.get(#idx).map_or(&b""[..], |c| c.as_bytes());
}
});
q_arms.push(
quote! {{
#statick;
if let Some(caps) = RE.captures(#value) {
#(#assigns);*
let output = Some(#then);
break 'switch output;
}
}}
);
}
quote! {{
'switch: {
#(#q_arms)*
None
}
}}.into()
}