mod batcher;
use darling::{ast::NestedMeta, FromMeta};
use proc_macro::TokenStream;
use proc_macro_crate::*;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Data, DeriveInput, Fields, ItemFn};
#[proc_macro_derive(Batcher, attributes(batch))]
pub fn batcher_derive(input: TokenStream) -> TokenStream {
batcher::batcher_derive(input)
}
#[derive(Debug, FromMeta)]
struct ConnectorFactoryArgs {
name: String,
#[darling(default)]
version: Option<String>,
#[darling(default)]
description: Option<String>,
#[darling(default)]
help_fn: Option<String>,
#[darling(default)]
target_list_fn: Option<String>,
#[darling(default)]
accept_input: bool,
#[darling(default)]
return_wrapped: bool,
#[darling(default)]
no_default_cache: bool,
}
#[derive(Debug, FromMeta)]
struct OsFactoryArgs {
name: String,
#[darling(default)]
version: Option<String>,
#[darling(default)]
description: Option<String>,
#[darling(default)]
help_fn: Option<String>,
#[darling(default)]
accept_input: bool,
#[darling(default)]
return_wrapped: bool,
}
fn validate_plugin_name(name: &str) {
if !name
.chars()
.all(|c| char::is_alphanumeric(c) || c == '-' || c == '_')
{
panic!("plugin name must only contain alphanumeric characters");
}
}
#[proc_macro_attribute]
pub fn connector(args: TokenStream, input: TokenStream) -> TokenStream {
let crate_path = crate_path();
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
Ok(v) => v,
Err(e) => return TokenStream::from(darling::Error::from(e).write_errors()),
};
let args = match ConnectorFactoryArgs::from_list(&attr_args) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let connector_name = args.name;
validate_plugin_name(&connector_name);
let version_gen = args
.version
.map_or_else(|| quote! { env!("CARGO_PKG_VERSION") }, |v| quote! { #v });
let description_gen = args.description.map_or_else(
|| quote! { env!("CARGO_PKG_DESCRIPTION") },
|d| quote! { #d },
);
let help_gen = if args.help_fn.is_some() {
quote! { Some(mf_help_callback) }
} else {
quote! { None }
};
let target_list_gen = if args.target_list_fn.is_some() {
quote! { Some(mf_target_list_callback) }
} else {
quote! { None }
};
let connector_descriptor: proc_macro2::TokenStream =
["MEMFLOW_CONNECTOR_", &connector_name.to_uppercase()]
.concat()
.parse()
.unwrap();
let func = parse_macro_input!(input as ItemFn);
let func_name = &func.sig.ident;
let func_accept_input = args.accept_input;
let func_return_wrapped = args.return_wrapped;
let no_default_cache = args.no_default_cache;
#[allow(clippy::collapsible_else_if)]
let create_fn_gen_inner = if func_accept_input {
if !func_return_wrapped {
quote! {
#crate_path::plugins::wrap_with_input(args, os.into(), lib, logger, out, |a, os, lib| {
Ok(#crate_path::plugins::connector::create_instance(#func_name(a, os)?, lib, a, #no_default_cache))
})
}
} else {
quote! {
#crate_path::plugins::wrap_with_input(args, os.into(), lib, logger, out, #func_name)
}
}
} else {
if !func_return_wrapped {
quote! {
#crate_path::plugins::wrap(args, lib, logger, out, |a, lib| {
Ok(#crate_path::plugins::connector::create_instance(#func_name(a)?, lib, a, #no_default_cache))
})
}
} else {
quote! {
#crate_path::plugins::wrap(args, lib, logger, out, #func_name)
}
}
};
let create_fn_gen = quote! {
#[doc(hidden)]
extern "C" fn mf_create(
args: Option<&#crate_path::plugins::connector::ConnectorArgs>,
os: #crate_path::cglue::option::COption<#crate_path::plugins::os::OsInstanceArcBox<'static>>,
lib: #crate_path::plugins::LibArc,
logger: Option<&'static #crate_path::plugins::PluginLogger>,
out: &mut #crate_path::plugins::connector::MuConnectorInstanceArcBox<'static>
) -> i32 {
#create_fn_gen_inner
}
};
let help_fn_gen = args.help_fn.map(|v| v.parse().unwrap()).map_or_else(
proc_macro2::TokenStream::new,
|func_name: proc_macro2::TokenStream| {
quote! {
#[doc(hidden)]
extern "C" fn mf_help_callback(
mut callback: #crate_path::plugins::HelpCallback,
) {
let helpstr = #func_name();
let _ = callback.call(helpstr.into());
}
}
},
);
let target_list_fn_gen = args.target_list_fn.map(|v| v.parse().unwrap()).map_or_else(
proc_macro2::TokenStream::new,
|func_name: proc_macro2::TokenStream| {
quote! {
#[doc(hidden)]
extern "C" fn mf_target_list_callback(
mut callback: #crate_path::plugins::TargetCallback,
) -> i32 {
#func_name()
.map(|mut targets| {
targets
.into_iter()
.take_while(|t| callback.call(t.clone()))
.for_each(|_| ());
})
.into_int_result()
}
}
},
);
let gen = quote! {
#[doc(hidden)]
#[no_mangle]
pub static #connector_descriptor: #crate_path::plugins::ConnectorDescriptor = #crate_path::plugins::ConnectorDescriptor {
plugin_version: #crate_path::plugins::MEMFLOW_PLUGIN_VERSION,
accept_input: #func_accept_input,
input_layout: <<#crate_path::plugins::LoadableConnector as #crate_path::plugins::Loadable>::CInputArg as #crate_path::abi_stable::StableAbi>::LAYOUT,
output_layout: <<#crate_path::plugins::LoadableConnector as #crate_path::plugins::Loadable>::Instance as #crate_path::abi_stable::StableAbi>::LAYOUT,
name: #crate_path::cglue::CSliceRef::from_str(#connector_name),
version: #crate_path::cglue::CSliceRef::from_str(#version_gen),
description: #crate_path::cglue::CSliceRef::from_str(#description_gen),
help_callback: #help_gen,
target_list_callback: #target_list_gen,
create: mf_create,
};
#create_fn_gen
#help_fn_gen
#target_list_fn_gen
#func
};
gen.into()
}
#[proc_macro_attribute]
pub fn os(args: TokenStream, input: TokenStream) -> TokenStream {
let crate_path = crate_path();
let attr_args = match NestedMeta::parse_meta_list(args.into()) {
Ok(v) => v,
Err(e) => return TokenStream::from(darling::Error::from(e).write_errors()),
};
let args = match OsFactoryArgs::from_list(&attr_args) {
Ok(v) => v,
Err(e) => return TokenStream::from(e.write_errors()),
};
let os_name = args.name;
validate_plugin_name(&os_name);
let version_gen = args
.version
.map_or_else(|| quote! { env!("CARGO_PKG_VERSION") }, |v| quote! { #v });
let description_gen = args.description.map_or_else(
|| quote! { env!("CARGO_PKG_DESCRIPTION") },
|d| quote! { #d },
);
let help_gen = if args.help_fn.is_some() {
quote! { Some(mf_help_callback) }
} else {
quote! { None }
};
let os_descriptor: proc_macro2::TokenStream = ["MEMFLOW_OS_", &os_name.to_uppercase()]
.concat()
.parse()
.unwrap();
let func = parse_macro_input!(input as ItemFn);
let func_name = &func.sig.ident;
let func_accept_input = args.accept_input;
let func_return_wrapped = args.return_wrapped;
#[allow(clippy::collapsible_else_if)]
let create_fn_gen_inner = if func_accept_input {
if !func_return_wrapped {
quote! {
#crate_path::plugins::wrap_with_input(args, connector.into(), lib, logger, out, |a, os, lib| {
Ok(#crate_path::plugins::os::create_instance(#func_name(a, os)?, lib, a))
})
}
} else {
quote! {
#crate_path::plugins::wrap_with_input(args, connector.into(), lib, logger, out, #func_name)
}
}
} else {
if !func_return_wrapped {
quote! {
#crate_path::plugins::wrap(args, lib, logger, out, |a, lib| {
Ok(#crate_path::plugins::os::create_instance(#func_name(a)?, lib, a))
})
}
} else {
quote! {
#crate_path::plugins::wrap(args, lib, logger, out, #func_name)
}
}
};
let create_fn_gen = quote! {
#[doc(hidden)]
extern "C" fn mf_create(
args: Option<&#crate_path::plugins::os::OsArgs>,
connector: #crate_path::cglue::COption<#crate_path::plugins::connector::ConnectorInstanceArcBox<'static>>,
lib: #crate_path::plugins::LibArc,
logger: Option<&'static #crate_path::plugins::PluginLogger>,
out: &mut #crate_path::plugins::os::MuOsInstanceArcBox<'static>
) -> i32 {
#create_fn_gen_inner
}
};
let help_fn_gen = args.help_fn.map(|v| v.parse().unwrap()).map_or_else(
proc_macro2::TokenStream::new,
|func_name: proc_macro2::TokenStream| {
quote! {
#[doc(hidden)]
extern "C" fn mf_help_callback(
mut callback: #crate_path::plugins::HelpCallback,
) {
let helpstr = #func_name();
let _ = callback.call(helpstr.into());
}
}
},
);
let gen = quote! {
#[doc(hidden)]
#[no_mangle]
pub static #os_descriptor: #crate_path::plugins::os::OsDescriptor = #crate_path::plugins::os::OsDescriptor {
plugin_version: #crate_path::plugins::MEMFLOW_PLUGIN_VERSION,
accept_input: #func_accept_input,
input_layout: <<#crate_path::plugins::os::LoadableOs as #crate_path::plugins::Loadable>::CInputArg as #crate_path::abi_stable::StableAbi>::LAYOUT,
output_layout: <<#crate_path::plugins::os::LoadableOs as #crate_path::plugins::Loadable>::Instance as #crate_path::abi_stable::StableAbi>::LAYOUT,
name: #crate_path::cglue::CSliceRef::from_str(#os_name),
version: #crate_path::cglue::CSliceRef::from_str(#version_gen),
description: #crate_path::cglue::CSliceRef::from_str(#description_gen),
help_callback: #help_gen,
target_list_callback: None, create: mf_create,
};
#create_fn_gen
#help_fn_gen
#func
};
gen.into()
}
#[proc_macro_derive(Pod)]
pub fn pod_derive(input: TokenStream) -> TokenStream {
let crate_path = crate_path();
format!("{crate_path}::dataview::derive_pod!{{ {input} }}")
.parse()
.unwrap()
}
#[proc_macro_derive(ByteSwap)]
pub fn byteswap_derive(input: TokenStream) -> TokenStream {
let crate_path = crate_path();
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let mut gen_inner = quote!();
match input.data {
Data::Struct(data) => match data.fields {
Fields::Named(named) => {
for field in named.named.iter() {
let name = field.ident.as_ref().unwrap();
gen_inner.extend(quote!(
self.#name.byte_swap();
));
}
}
_ => unimplemented!(),
},
_ => unimplemented!(),
};
let gen = quote!(
impl #impl_generics #crate_path::types::byte_swap::ByteSwap for #name #ty_generics #where_clause {
fn byte_swap(&mut self) {
#gen_inner
}
}
);
gen.into()
}
fn crate_path() -> proc_macro2::TokenStream {
let (col, ident) = crate_path_ident();
quote!(#col #ident)
}
fn crate_path_ident() -> (Option<syn::token::PathSep>, proc_macro2::Ident) {
let found_crate = crate_name("memflow").expect("memflow found in `Cargo.toml`");
match found_crate {
FoundCrate::Itself => (None, format_ident!("memflow")), FoundCrate::Name(name) => (Some(Default::default()), format_ident!("{}", name)),
}
}