use proc_macro2;
use quote::{format_ident, quote};
use syn::Ident;
pub fn to_context_expr(ident_name: &str) -> (Ident, proc_macro2::TokenStream) {
converted_ident(ident_name, to_context_expr2)
}
fn to_context_expr2(ident: &Ident) -> proc_macro2::TokenStream {
quote! {
::milter::Context::new(#ident)
}
}
pub fn to_str_expr(ident_name: &str, result_ret: bool) -> (Ident, proc_macro2::TokenStream) {
if result_ret {
converted_ident(ident_name, to_str_expr_result)
} else {
converted_ident(ident_name, to_str_expr_panic)
}
}
fn to_str_expr_result(ident: &Ident) -> proc_macro2::TokenStream {
quote! {
match ::std::ffi::CStr::from_ptr(#ident).to_str() {
::std::result::Result::Ok(s) => s,
::std::result::Result::Err(e) => {
return ::std::result::Result::Err(::milter::Error::new(::milter::ErrorKind::TypeConversion, e));
}
}
}
}
fn to_str_expr_panic(ident: &Ident) -> proc_macro2::TokenStream {
quote! {
::std::ffi::CStr::from_ptr(#ident).to_str().expect("invalid string in char pointer")
}
}
pub fn to_strs_expr(ident_name: &str, result_ret: bool) -> (Ident, proc_macro2::TokenStream) {
if result_ret {
converted_ident(ident_name, to_strs_expr_result)
} else {
converted_ident(ident_name, to_strs_expr_panic)
}
}
fn to_strs_expr_result(ident: &Ident) -> proc_macro2::TokenStream {
quote! {
match (0..)
.map(|i| *#ident.offset(i))
.take_while(|p| !p.is_null())
.map(|p| ::std::ffi::CStr::from_ptr(p).to_str())
.collect::<::std::result::Result<::std::vec::Vec<_>, _>>() {
::std::result::Result::Ok(s) => s,
::std::result::Result::Err(e) => {
return ::std::result::Result::Err(::milter::Error::new(::milter::ErrorKind::TypeConversion, e));
}
}
}
}
fn to_strs_expr_panic(ident: &Ident) -> proc_macro2::TokenStream {
quote! {
(0..)
.map(|i| *#ident.offset(i))
.take_while(|p| !p.is_null())
.map(|p| ::std::ffi::CStr::from_ptr(p).to_str().expect("invalid string in char pointer"))
.collect::<::std::vec::Vec<_>>()
}
}
pub fn to_socket_addr_expr(ident_name: &str) -> (Ident, proc_macro2::TokenStream) {
converted_ident(ident_name, to_socket_addr_expr2)
}
fn to_socket_addr_expr2(ident: &Ident) -> proc_macro2::TokenStream {
quote! {
if #ident.is_null() {
::std::option::Option::None
} else {
match (*#ident).sa_family as _ {
::libc::AF_INET => {
let addr = #ident as *const ::libc::sockaddr_in;
let ip = ::std::net::Ipv4Addr::from(u32::from_be((*addr).sin_addr.s_addr));
let port = u16::from_be((*addr).sin_port);
::std::option::Option::Some(::std::net::SocketAddr::from(::std::net::SocketAddrV4::new(ip, port)))
}
::libc::AF_INET6 => {
let addr = #ident as *const ::libc::sockaddr_in6;
let ip = ::std::net::Ipv6Addr::from((*addr).sin6_addr.s6_addr);
let port = u16::from_be((*addr).sin6_port);
let flowinfo = (*addr).sin6_flowinfo;
let scope_id = (*addr).sin6_scope_id;
::std::option::Option::Some(::std::net::SocketAddr::from(::std::net::SocketAddrV6::new(ip, port, flowinfo, scope_id)))
}
_ => ::std::option::Option::None,
}
}
}
}
fn converted_ident(
ident_name: &str,
convert_fn: impl Fn(&Ident) -> proc_macro2::TokenStream,
) -> (Ident, proc_macro2::TokenStream) {
let ident = format_ident!("{}", ident_name);
let converted_expr = convert_fn(&ident);
(ident, converted_expr)
}
pub fn invoke_handler_expr(
handler: proc_macro2::TokenStream,
result_ret: bool,
) -> proc_macro2::TokenStream {
let ok_arms = quote! {
::std::result::Result::Ok(status) => status as ::milter::sfsistat,
};
invoke_handler_expr_ext(handler, result_ret, ok_arms)
}
pub fn invoke_handler_expr_ext(
handler: proc_macro2::TokenStream,
result_ret: bool,
mut ok_arms: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
if result_ret {
ok_arms = quote! {
::std::result::Result::Ok(result) => match result {
#ok_arms
::std::result::Result::Err(err) => {
eprintln!("error result in milter callback: {}", err);
::milter::Status::Tempfail as ::milter::sfsistat
}
},
};
}
quote! {
if ::milter::is_panicked() {
::milter::Status::Tempfail as ::milter::sfsistat
} else {
match ::std::panic::catch_unwind(|| { #handler }) {
#ok_arms
::std::result::Result::Err(_) => {
::milter::set_panicked(true);
eprintln!("panic in milter callback, shutting down");
::milter::shutdown();
::milter::Status::Tempfail as ::milter::sfsistat
}
}
}
}
}