fix #[export(name=...)], drop deprecated make_package

The `make_package` macro is tedious to use. We now simply
depend on new-enough rustc for `package` to work.

The change to #[export] allows writing:

    #[export(name = "DESTROY")]
    fn destroy(#[raw] this: Value) { ... }

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller
2020-11-25 14:35:34 +01:00
parent ca00cfcc9a
commit 2991a46a31
10 changed files with 31 additions and 214 deletions

View File

@ -64,7 +64,9 @@ impl ModuleAttrs {
} }
} }
#[derive(Default)]
pub struct FunctionAttrs { pub struct FunctionAttrs {
pub perl_name: Option<Ident>,
pub xs_name: Option<Ident>, pub xs_name: Option<Ident>,
pub raw_return: bool, pub raw_return: bool,
} }
@ -73,8 +75,7 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
type Error = syn::Error; type Error = syn::Error;
fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> { fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
let mut xs_name = None; let mut attrs = FunctionAttrs::default();
let mut raw_return = false;
for arg in args { for arg in args {
match arg { match arg {
@ -83,15 +84,17 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
lit: syn::Lit::Str(litstr), lit: syn::Lit::Str(litstr),
.. ..
})) => { })) => {
if path.is_ident("name") { if path.is_ident("xs_name") {
xs_name = Some(Ident::new(&litstr.value(), litstr.span())); 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 { } else {
bail!(path => "unknown argument"); bail!(path => "unknown argument");
} }
} }
syn::NestedMeta::Meta(syn::Meta::Path(path)) => { syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
if path.is_ident("raw_return") { if path.is_ident("raw_return") {
raw_return = true; attrs.raw_return = true;
} else { } else {
bail!(path => "unknown attribute"); bail!(path => "unknown attribute");
} }
@ -100,9 +103,6 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
} }
} }
Ok(Self { Ok(attrs)
xs_name,
raw_return,
})
} }
} }

View File

@ -8,6 +8,7 @@ use crate::attribs::FunctionAttrs;
pub struct XSub { pub struct XSub {
pub rust_name: Ident, pub rust_name: Ident,
pub perl_name: Option<Ident>,
pub xs_name: Ident, pub xs_name: Ident,
pub tokens: TokenStream, pub tokens: TokenStream,
} }
@ -201,6 +202,7 @@ pub fn handle_function(
#wrapper_func #wrapper_func
#[inline(never)] #[inline(never)]
#[allow(non_snake_case)]
fn #impl_xs_name( fn #impl_xs_name(
_cv: &::perlmod::ffi::CV, _cv: &::perlmod::ffi::CV,
) -> Result<#return_type, *mut ::perlmod::ffi::SV> { ) -> Result<#return_type, *mut ::perlmod::ffi::SV> {
@ -229,6 +231,7 @@ pub fn handle_function(
Ok(XSub { Ok(XSub {
rust_name: name.to_owned(), rust_name: name.to_owned(),
perl_name: attr.perl_name.clone(),
xs_name, xs_name,
tokens, tokens,
}) })

View File

@ -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 /// 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] #[proc_macro_attribute]
pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 { pub fn export(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
let attr = parse_macro_input!(attr as AttributeArgs); 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() 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<u32, Error> {
/// 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<u32, Error> {
/// 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<TokenStream, Error> { fn perlmod_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Error> {
let item: syn::Item = syn::parse2(item)?; let item: syn::Item = syn::parse2(item)?;
@ -161,9 +99,3 @@ fn export_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Er
let func = function::handle_function(attr, func, None)?; let func = function::handle_function(attr, func, None)?;
Ok(func.tokens) Ok(func.tokens)
} }
fn make_package_impl(item: TokenStream) -> Result<TokenStream, Error> {
let pkg: package::Package = syn::parse2(item)?;
pkg.write()?;
Ok(TokenStream::new())
}

View File

@ -55,6 +55,7 @@ pub fn handle_module(attr: AttributeArgs, mut module: syn::ItemMod) -> Result<To
package.export_named( package.export_named(
func.rust_name, func.rust_name,
func.perl_name,
func.xs_name, func.xs_name,
"src/FIXME.rs".to_string(), "src/FIXME.rs".to_string(),
); );

View File

@ -8,10 +8,7 @@ use anyhow::Error;
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::AttributeArgs; use syn::AttributeArgs;
use syn::Token;
use toml::Value; use toml::Value;
@ -56,6 +53,7 @@ const MODULE_TAIL: &str = "}\n";
struct Export { struct Export {
rust_name: Ident, rust_name: Ident,
perl_name: Option<Ident>,
xs_name: Ident, xs_name: Ident,
file_name: String, 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<Ident>,
xs_name: Ident,
file_name: String,
) {
self.exported.push(Export { self.exported.push(Export {
rust_name, rust_name,
xs_name, perl_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,
xs_name, xs_name,
file_name, file_name,
}); });
@ -97,7 +93,7 @@ impl Package {
source = format!( source = format!(
"{} newXS('{}', '{}', \"{}\");\n", "{} newXS('{}', '{}', \"{}\");\n",
source, source,
export.rust_name, export.perl_name.as_ref().unwrap_or(&export.rust_name),
export.xs_name, export.xs_name,
export.file_name.replace('"', "\\\""), 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<Self> {
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<ExportItem, Token![,]> =
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<Self> {
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<HashMap<String, Value>, syn::Error> { fn read_cargo_toml(why: Span) -> Result<HashMap<String, Value>, syn::Error> {
let manifest_dir = env::var("CARGO_MANIFEST_DIR") let manifest_dir = env::var("CARGO_MANIFEST_DIR")
.map_err(|err| format_err!(why, "failed to get CARGO_MANIFEST_DIR variable: {}", err))?; .map_err(|err| format_err!(why, "failed to get CARGO_MANIFEST_DIR variable: {}", err))?;

View File

@ -26,8 +26,8 @@ mod export {
println!("Called something!"); println!("Called something!");
} }
#[export] #[export(name = "DESTROY")]
fn DESTROY(#[raw] this: Value) { fn destroy(#[raw] this: Value) {
match this match this
.dereference() .dereference()
.ok_or_else(|| format_err!("not a reference")) .ok_or_else(|| format_err!("not a reference"))

View File

@ -7,8 +7,5 @@ mod pkg142;
/// possible on nightly with #![feature(custom_inner_attributes)] /// possible on nightly with #![feature(custom_inner_attributes)]
mod pkginline; mod pkginline;
/// This is possible on stable rust with some 1.3x already.
mod pkgstable;
/// A test for blessed values. /// A test for blessed values.
mod bless; mod bless;

View File

@ -1,26 +0,0 @@
use anyhow::{bail, Error};
#[perlmod::export]
fn foo(a: u32, b: u32) -> Result<u32, Error> {
if a == 42 {
bail!("dying on magic number");
}
Ok(a + b)
}
#[perlmod::export(name = "xs_a")]
fn func_b(a: u32) -> Result<u32, Error> {
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
}
}

View File

@ -1,17 +1,13 @@
//! Crate for creating perl packages/bindings for rust code. //! Crate for creating perl packages/bindings for rust code.
//! //!
//! The main feature of this crate are the [`package`], [`export`] and [`make_package!`] macros //! The main feature of this crate is the [`package`] macro provided by the `perlmod-macro` crate
//! provided by the `perlmod-macro` crate. These are documented here. //! and documented here.
//! //!
//! The underlying machinery for these macros is contained in this crate and provides ways to //! The underlying machinery for these macros is contained in this crate and provides ways to
//! serialize and deserialize data between perl and rust. //! 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 //! [`package`]: attr.package.html
//! [`export`]: attr.export.html //! [`export`]: attr.export.html
//! [`make_package!`]: macro.make_package.html
pub(crate) mod error; pub(crate) mod error;
pub use error::Error; pub use error::Error;
@ -43,4 +39,4 @@ pub use value::Value;
#[cfg(feature = "exporter")] #[cfg(feature = "exporter")]
#[doc(inline)] #[doc(inline)]
pub use perlmod_macro::{export, make_package, package}; pub use perlmod_macro::{export, package};

View File

@ -272,7 +272,7 @@ impl ScalarRef {
/// ///
/// The user is responsible for making sure the underlying pointer is correct. /// The user is responsible for making sure the underlying pointer is correct.
pub unsafe fn pv_ref<T>(&self) -> Result<&T, Error> { pub unsafe fn pv_ref<T>(&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 /// 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. /// The user is responsible for making sure the underlying pointer is correct.
pub unsafe fn pv_mut_ref<T>(&self) -> Result<&mut T, Error> { pub unsafe fn pv_mut_ref<T>(&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. /// Create another owned reference to this value.