remove the need to list all exported functions when declaring the module

This commit is contained in:
Robin Appelman 2019-04-05 11:02:14 +02:00
commit 34fb187384
7 changed files with 22 additions and 64 deletions

View file

@ -21,7 +21,6 @@ fn hello_world() {
ivory_module!({
name: "demo",
version: "0.0.1",
functions: &[hello_world, hello_other],
info: &[("demo extension", "enabled")]
});
```

View file

@ -1,5 +1,5 @@
use ivory::{ivory_export, ivory_module};
use ivory::externs::printf;
use ivory::{ivory_export, ivory_module};
#[ivory_export]
fn hello_other(other: String) {
@ -14,6 +14,5 @@ fn hello_world() {
ivory_module!({
name: "demo",
version: "0.0.1",
functions: &[hello_world, hello_other],
info: &[("demo extension", "enabled")]
});
});

View file

@ -12,6 +12,7 @@ edition = "2018"
syn = { version = "0.15", features = ["full"] }
quote = "0.6"
proc-macro2 = "0.4"
lazy_static = "1.3"
[dev-dependencies]
ivory = { version = "0.1", path = ".." }

14
ivory/macro/src/cache.rs Normal file
View file

@ -0,0 +1,14 @@
use lazy_static::lazy_static;
use std::sync::Mutex;
lazy_static! {
static ref FUNCTION_NAMES: Mutex<Vec<String>> = Mutex::new(Vec::new());
}
pub fn cache_function(name: String) {
FUNCTION_NAMES.lock().unwrap().push(name);
}
pub fn get_functions() -> Vec<String> {
FUNCTION_NAMES.lock().unwrap().clone()
}

View file

@ -2,14 +2,12 @@
extern crate proc_macro;
mod cache;
use proc_macro2::{Span, TokenStream, TokenTree};
use quote::quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{
parse2, parse_macro_input, AttributeArgs, Expr, FieldValue, FnArg, Ident, Item, ItemFn, LitStr,
Pat, Type,
};
use syn::{parse_macro_input, AttributeArgs, FnArg, Ident, Item, ItemFn, LitStr, Pat, Type};
/// See the [crate documentation](index.html) for details
#[proc_macro_attribute]
@ -35,6 +33,7 @@ fn export_fn(item: ItemFn) -> TokenStream {
let span = item.span();
let name = item.ident;
let name_str = name.to_string();
cache::cache_function(name_str.clone());
let meta_name = Ident::new(&format!("FUNCTION_META_{}", name_str.to_uppercase()), span);
let body = item.block;
let decl = item.decl;
@ -122,13 +121,7 @@ pub fn ivory_module(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let fields = group.stream();
let struct_def = quote! {
::ivory::zend::PhpModule {
#fields
}
};
let function_names = get_function_names(struct_def);
let funcs = get_funcs(function_names, span);
let funcs = get_funcs(cache::get_functions(), span);
let fields = into_c_str(fields);
@ -195,45 +188,6 @@ fn into_c_str(input: TokenStream) -> TokenStream {
output
}
fn get_function_names(struct_def: TokenStream) -> Vec<String> {
let expr: Expr = parse2(struct_def).unwrap();
let expr = get_field_expr(expr, "functions").unwrap();
match expr {
Expr::Reference(ref_expr) => match *ref_expr.expr {
Expr::Array(arr) => arr
.elems
.into_iter()
.map(|element: Expr| {
let tokens: TokenStream = quote!(#element);
let tree = tokens.into_iter().next().unwrap();
match tree {
TokenTree::Ident(ident) => ident.to_string(),
_ => panic!(),
}
})
.collect(),
_ => panic!(),
},
_ => panic!(),
}
}
fn get_field_expr(expr: Expr, field_name: &str) -> Option<Expr> {
let fields: Punctuated<FieldValue, syn::token::Comma> = match expr {
Expr::Struct(expr) => expr.fields,
_ => panic!("invalid struct"),
};
for field in fields {
if let syn::Member::Named(ident) = &field.member {
let name = ident.to_string();
if &name == field_name {
return Some(field.expr);
}
}
}
None
}
fn get_funcs(names: Vec<String>, span: Span) -> TokenStream {
let definitions = names.into_iter().map(|name| {
let meta_name = Ident::new(&format!("FUNCTION_META_{}", name.to_uppercase()), span);

View file

@ -112,6 +112,5 @@ impl FunctionMeta {
pub struct PhpModule {
pub name: *const c_char,
pub version: *const c_char,
pub functions: &'static [HandlerFunc],
pub info: &'static [(&'static str, &'static str)],
}

View file

@ -41,13 +41,5 @@ fn expect_option_bool(arg: Option<bool>) {
ivory_module!({
name: "tests",
version: "0.0.1",
functions: &[
dump_arg,
expect_long,
expect_double,
expect_string,
expect_bool,
expect_option_bool
],
info: &[("test extension", "enabled")]
});