diff --git a/perlmod-macro/src/attribs.rs b/perlmod-macro/src/attribs.rs index 34b55e0..7c77fab 100644 --- a/perlmod-macro/src/attribs.rs +++ b/perlmod-macro/src/attribs.rs @@ -64,7 +64,9 @@ impl ModuleAttrs { } } +#[derive(Default)] pub struct FunctionAttrs { + pub perl_name: Option, pub xs_name: Option, pub raw_return: bool, } @@ -73,8 +75,7 @@ impl TryFrom for FunctionAttrs { type Error = syn::Error; fn try_from(args: AttributeArgs) -> Result { - let mut xs_name = None; - let mut raw_return = false; + let mut attrs = FunctionAttrs::default(); for arg in args { match arg { @@ -83,15 +84,17 @@ impl TryFrom for FunctionAttrs { lit: syn::Lit::Str(litstr), .. })) => { - if path.is_ident("name") { - xs_name = Some(Ident::new(&litstr.value(), litstr.span())); + if path.is_ident("xs_name") { + attrs.xs_name = Some(Ident::new(&litstr.value(), litstr.span())); + } else if path.is_ident("name") { + attrs.perl_name = Some(Ident::new(&litstr.value(), litstr.span())); } else { bail!(path => "unknown argument"); } } syn::NestedMeta::Meta(syn::Meta::Path(path)) => { if path.is_ident("raw_return") { - raw_return = true; + attrs.raw_return = true; } else { bail!(path => "unknown attribute"); } @@ -100,9 +103,6 @@ impl TryFrom for FunctionAttrs { } } - Ok(Self { - xs_name, - raw_return, - }) + Ok(attrs) } } diff --git a/perlmod-macro/src/function.rs b/perlmod-macro/src/function.rs index c5c8d2a..a845ad6 100644 --- a/perlmod-macro/src/function.rs +++ b/perlmod-macro/src/function.rs @@ -8,6 +8,7 @@ use crate::attribs::FunctionAttrs; pub struct XSub { pub rust_name: Ident, + pub perl_name: Option, pub xs_name: Ident, pub tokens: TokenStream, } @@ -201,6 +202,7 @@ pub fn handle_function( #wrapper_func #[inline(never)] + #[allow(non_snake_case)] fn #impl_xs_name( _cv: &::perlmod::ffi::CV, ) -> Result<#return_type, *mut ::perlmod::ffi::SV> { @@ -229,6 +231,7 @@ pub fn handle_function( Ok(XSub { rust_name: name.to_owned(), + perl_name: attr.perl_name.clone(), xs_name, tokens, }) diff --git a/perlmod-macro/src/lib.rs b/perlmod-macro/src/lib.rs index 64ec05b..045e33a 100644 --- a/perlmod-macro/src/lib.rs +++ b/perlmod-macro/src/lib.rs @@ -74,7 +74,7 @@ pub fn package(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 { } /// Attribute to export a function so that it can be installed as an `xsub` in perl. See the -/// [`make_package!`] macro for a usage example. +/// [`package!`] macro for a usage example. #[proc_macro_attribute] pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 { let attr = parse_macro_input!(attr as AttributeArgs); @@ -82,68 +82,6 @@ pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 { handle_error(item.clone(), export_impl(attr, item)).into() } -/// Proc macro to create a perl package file for rust functions. -/// -/// This macro will write a perl package/module file into cargo's working directory. (Typically the -/// manifest directory.) -/// -/// This macro exists mostly for backward compatibility. When using rustc 1.42 or above, a more -/// readable and less repetitive code will be produced with the [`package`](module@crate::package) -/// attribute instead. -/// -/// This macro always has to be used in conjunction with the [`export!]` macro, like this: -/// -/// ``` -/// # mod testmod { -/// use anyhow::{bail, Error}; -/// use perlmod::export; -/// -/// #[export] -/// fn sum_except_42(a: u32, b: u32) -> Result { -/// if a == 42 { -/// // Errors 'die' in perl, so newlines at the end of error messages make a difference! -/// bail!("dying on magic number\n"); -/// } -/// -/// Ok(a + b) -/// } -/// -/// #[export(name = "xs_sub_name")] -/// fn double(a: u32) -> Result { -/// Ok(2 * a) -/// } -/// -/// perlmod::make_package! { -/// // First we need to specify the package, similar to perl's syntax: -/// package "RSPM::DocTest1"; -/// -/// // The library name is usually derived from the crate name in Cargo.toml automatically. -/// // So this line is optional: -/// lib "perlmod_test"; -/// -/// // An optional output file name can be specified as follows: -/// // (we use this here to prevent doc tests from creating files...) -/// file "/dev/null"; -/// -/// // The list of xsubs we want to export: -/// subs { -/// // When only providing the name, default naming convention will be used: -/// // This is used like: `RSPM::DocTest1::sum_except_42(4, 5);` in perl. -/// sum_except_42, -/// // If we used an explicit export name, we need to also explicitly export the renamed -/// // function here: -/// // This is used like: `RSPM::DocTest1::double_the_number(5);` in perl. -/// xs_sub_name as double_the_number, -/// } -/// } -/// # } -/// ``` -#[proc_macro] -pub fn make_package(item: TokenStream_1) -> TokenStream_1 { - let item: TokenStream = item.into(); - handle_error(item.clone(), make_package_impl(item)).into() -} - fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result { let item: syn::Item = syn::parse2(item)?; @@ -161,9 +99,3 @@ fn export_impl(attr: AttributeArgs, item: TokenStream) -> Result Result { - let pkg: package::Package = syn::parse2(item)?; - pkg.write()?; - Ok(TokenStream::new()) -} diff --git a/perlmod-macro/src/module.rs b/perlmod-macro/src/module.rs index bb4bb90..f3aae4c 100644 --- a/perlmod-macro/src/module.rs +++ b/perlmod-macro/src/module.rs @@ -55,6 +55,7 @@ pub fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result, xs_name: Ident, file_name: String, } @@ -73,18 +71,16 @@ impl Package { }) } - pub fn export_named(&mut self, rust_name: Ident, xs_name: Ident, file_name: String) { + pub fn export_named( + &mut self, + rust_name: Ident, + perl_name: Option, + xs_name: Ident, + file_name: String, + ) { self.exported.push(Export { rust_name, - xs_name, - file_name, - }); - } - - pub fn export_direct(&mut self, name: Ident, file_name: String) { - let xs_name = Ident::new(&format!("xs_{}", name), name.span()); - self.exported.push(Export { - rust_name: name, + perl_name, xs_name, file_name, }); @@ -97,7 +93,7 @@ impl Package { source = format!( "{} newXS('{}', '{}', \"{}\");\n", source, - export.rust_name, + export.perl_name.as_ref().unwrap_or(&export.rust_name), export.xs_name, export.file_name.replace('"', "\\\""), ); @@ -132,88 +128,6 @@ impl Package { } } -mod kw { - syn::custom_keyword!(package); - syn::custom_keyword!(lib); - syn::custom_keyword!(file); - syn::custom_keyword!(subs); -} - -impl Parse for Package { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let mut pkg = Package { - attrs: ModuleAttrs { - package_name: String::new(), - file_name: None, - lib_name: None, - }, - exported: Vec::new(), - }; - - // `package "Package::Name";` comes first - let _pkg: kw::package = input.parse()?; - let package: syn::LitStr = input.parse()?; - let _semicolon: Token![;] = input.parse()?; - pkg.attrs.package_name = package.value(); - - // `lib "lib_name";` optionally comes second - let lookahead = input.lookahead1(); - if lookahead.peek(kw::lib) { - let _lib: kw::lib = input.parse()?; - let lib: syn::LitStr = input.parse()?; - pkg.attrs.lib_name = Some(lib.value()); - let _semicolon: Token![;] = input.parse()?; - } - drop(lookahead); - - // `file "File/Name.pm";` optionally comes third - let lookahead = input.lookahead1(); - if lookahead.peek(kw::file) { - let _file: kw::file = input.parse()?; - let file: syn::LitStr = input.parse()?; - pkg.attrs.file_name = Some(file.value()); - let _semicolon: Token![;] = input.parse()?; - } - drop(lookahead); - - // `sub { ... }` must follow: - let _sub: kw::subs = input.parse()?; - let content; - let _brace_token: syn::token::Brace = syn::braced!(content in input); - let items: Punctuated = - content.parse_terminated(ExportItem::parse)?; - - for item in items { - match item { - ExportItem::Direct(name) => pkg.export_direct(name, "src/FIXME.rs".to_string()), - ExportItem::Named(name, as_name) => { - pkg.export_named(as_name, name, "src/FIXME.rs".to_string()); - } - } - } - - Ok(pkg) - } -} - -enum ExportItem { - Direct(Ident), - Named(Ident, Ident), -} - -impl Parse for ExportItem { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let name: Ident = input.parse()?; - let lookahead = input.lookahead1(); - if lookahead.peek(syn::token::As) { - let _as: syn::token::As = input.parse()?; - Ok(ExportItem::Named(name, input.parse()?)) - } else { - Ok(ExportItem::Direct(name)) - } - } -} - fn read_cargo_toml(why: Span) -> Result, syn::Error> { let manifest_dir = env::var("CARGO_MANIFEST_DIR") .map_err(|err| format_err!(why, "failed to get CARGO_MANIFEST_DIR variable: {}", err))?; diff --git a/perlmod-test/src/bless.rs b/perlmod-test/src/bless.rs index e79ed37..c973938 100644 --- a/perlmod-test/src/bless.rs +++ b/perlmod-test/src/bless.rs @@ -26,8 +26,8 @@ mod export { println!("Called something!"); } - #[export] - fn DESTROY(#[raw] this: Value) { + #[export(name = "DESTROY")] + fn destroy(#[raw] this: Value) { match this .dereference() .ok_or_else(|| format_err!("not a reference")) diff --git a/perlmod-test/src/lib.rs b/perlmod-test/src/lib.rs index a10c2e8..9935a8d 100644 --- a/perlmod-test/src/lib.rs +++ b/perlmod-test/src/lib.rs @@ -7,8 +7,5 @@ mod pkg142; /// possible on nightly with #![feature(custom_inner_attributes)] mod pkginline; -/// This is possible on stable rust with some 1.3x already. -mod pkgstable; - /// A test for blessed values. mod bless; diff --git a/perlmod-test/src/pkgstable.rs b/perlmod-test/src/pkgstable.rs deleted file mode 100644 index 1ecfafc..0000000 --- a/perlmod-test/src/pkgstable.rs +++ /dev/null @@ -1,26 +0,0 @@ -use anyhow::{bail, Error}; - -#[perlmod::export] -fn foo(a: u32, b: u32) -> Result { - if a == 42 { - bail!("dying on magic number"); - } - - Ok(a + b) -} - -#[perlmod::export(name = "xs_a")] -fn func_b(a: u32) -> Result { - Ok(a * 2) -} - -perlmod::make_package! { - package "RSPM::Foo"; - - //lib "perlmod_test"; - - subs { - foo, - xs_a as b, // func_b's exported xsub was renamed to xs_a, and in perl it's called b - } -} diff --git a/perlmod/src/lib.rs b/perlmod/src/lib.rs index d7b3aa0..e5211eb 100644 --- a/perlmod/src/lib.rs +++ b/perlmod/src/lib.rs @@ -1,17 +1,13 @@ //! Crate for creating perl packages/bindings for rust code. //! -//! The main feature of this crate are the [`package`], [`export`] and [`make_package!`] macros -//! provided by the `perlmod-macro` crate. These are documented here. +//! The main feature of this crate is the [`package`] macro provided by the `perlmod-macro` crate +//! and documented here. //! //! The underlying machinery for these macros is contained in this crate and provides ways to //! serialize and deserialize data between perl and rust. //! -//! For now, see the [`make_package!`] macro for all the details, since as of the time of writing -//! this, we're only at rust 1.40. -//! //! [`package`]: attr.package.html //! [`export`]: attr.export.html -//! [`make_package!`]: macro.make_package.html pub(crate) mod error; pub use error::Error; @@ -43,4 +39,4 @@ pub use value::Value; #[cfg(feature = "exporter")] #[doc(inline)] -pub use perlmod_macro::{export, make_package, package}; +pub use perlmod_macro::{export, package}; diff --git a/perlmod/src/scalar.rs b/perlmod/src/scalar.rs index bbd7f93..49705b7 100644 --- a/perlmod/src/scalar.rs +++ b/perlmod/src/scalar.rs @@ -272,7 +272,7 @@ impl ScalarRef { /// /// The user is responsible for making sure the underlying pointer is correct. pub unsafe fn pv_ref(&self) -> Result<&T, Error> { - self.pv_raw().map(|p| unsafe { &*p }) + self.pv_raw().map(|p| &*p) } /// Interpret the byte string as a pointer and return it as a mutable reference for @@ -282,7 +282,7 @@ impl ScalarRef { /// /// The user is responsible for making sure the underlying pointer is correct. pub unsafe fn pv_mut_ref(&self) -> Result<&mut T, Error> { - self.pv_raw().map(|p| unsafe { &mut *p }) + self.pv_raw().map(|p| &mut *p) } /// Create another owned reference to this value.