}
}
+#[derive(Default)]
pub struct FunctionAttrs {
+ pub perl_name: Option<Ident>,
pub xs_name: Option<Ident>,
pub raw_return: bool,
}
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 {
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");
}
}
}
- Ok(Self {
- xs_name,
- raw_return,
- })
+ Ok(attrs)
}
}
pub struct XSub {
pub rust_name: Ident,
+ pub perl_name: Option<Ident>,
pub xs_name: Ident,
pub tokens: TokenStream,
}
#wrapper_func
#[inline(never)]
+ #[allow(non_snake_case)]
fn #impl_xs_name(
_cv: &::perlmod::ffi::CV,
) -> Result<#return_type, *mut ::perlmod::ffi::SV> {
Ok(XSub {
rust_name: name.to_owned(),
+ perl_name: attr.perl_name.clone(),
xs_name,
tokens,
})
}
/// 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);
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)?;
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())
-}
package.export_named(
func.rust_name,
+ func.perl_name,
func.xs_name,
"src/FIXME.rs".to_string(),
);
use proc_macro2::{Ident, Span};
-use syn::parse::Parse;
-use syn::punctuated::Punctuated;
use syn::AttributeArgs;
-use syn::Token;
use toml::Value;
struct Export {
rust_name: Ident,
+ perl_name: Option<Ident>,
xs_name: Ident,
file_name: String,
}
})
}
- 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,
});
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('"', "\\\""),
);
}
}
-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))?;
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"))
/// 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;
+++ /dev/null
-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
- }
-}
//! 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;
#[cfg(feature = "exporter")]
#[doc(inline)]
-pub use perlmod_macro::{export, make_package, package};
+pub use perlmod_macro::{export, package};
///
/// 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
///
/// 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.