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 perl_name: Option<Ident>,
pub xs_name: Option<Ident>,
pub raw_return: bool,
}
@ -73,8 +75,7 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
type Error = syn::Error;
fn try_from(args: AttributeArgs) -> Result<Self, Self::Error> {
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<AttributeArgs> 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<AttributeArgs> for FunctionAttrs {
}
}
Ok(Self {
xs_name,
raw_return,
})
Ok(attrs)
}
}

View File

@ -8,6 +8,7 @@ use crate::attribs::FunctionAttrs;
pub struct XSub {
pub rust_name: Ident,
pub perl_name: Option<Ident>,
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,
})

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
/// [`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<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> {
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)?;
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(
func.rust_name,
func.perl_name,
func.xs_name,
"src/FIXME.rs".to_string(),
);

View File

@ -8,10 +8,7 @@ use anyhow::Error;
use proc_macro2::{Ident, Span};
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::AttributeArgs;
use syn::Token;
use toml::Value;
@ -56,6 +53,7 @@ const MODULE_TAIL: &str = "}\n";
struct Export {
rust_name: Ident,
perl_name: Option<Ident>,
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<Ident>,
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<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> {
let manifest_dir = env::var("CARGO_MANIFEST_DIR")
.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!");
}
#[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"))

View File

@ -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;

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.
//!
//! 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};

View File

@ -272,7 +272,7 @@ impl ScalarRef {
///
/// The user is responsible for making sure the underlying pointer is correct.
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
@ -282,7 +282,7 @@ impl ScalarRef {
///
/// The user is responsible for making sure the underlying pointer is correct.
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.