make_package macro implementation

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2020-01-10 10:47:42 +01:00
parent b03c149435
commit 06a18771dd
8 changed files with 167 additions and 23 deletions

View File

@ -6,7 +6,7 @@ use syn::AttributeArgs;
pub struct ModuleAttrs {
pub package_name: String,
pub file_name: String,
pub file_name: Option<String>,
pub lib_name: Option<String>,
}
@ -42,9 +42,6 @@ impl TryFrom<AttributeArgs> for ModuleAttrs {
let package_name = package_name
.ok_or_else(|| format_err!(Span::call_site(), "missing 'package' argument"))?;
let file_name =
file_name.unwrap_or_else(|| format!("{}.pm", package_name.replace("::", "/")));
Ok(Self {
package_name,
file_name,

View File

@ -55,12 +55,12 @@ 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.
// #[proc_macro]
// pub fn make_package(item: TokenStream_1) -> TokenStream_1 {
// let item: TokenStream = item.into();
// handle_error(item.clone(), make_package_impl(attr, item)).into()
// }
/// Proc macro to create a perl package file for rust functions.
#[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)?;
@ -79,3 +79,9 @@ fn export_impl(attr: AttributeArgs, item: TokenStream) -> Result<TokenStream, Er
let func = function::handle_function(attr, func)?;
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

@ -4,7 +4,10 @@ use failure::Error;
use proc_macro2::Ident;
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::AttributeArgs;
use syn::Token;
use crate::attribs::ModuleAttrs;
@ -104,7 +107,13 @@ impl Package {
source = source.replace("{{LIB_NAME}}", LIB_NAME_DEFAULT);
}
let path = std::path::Path::new(&self.attrs.file_name);
let file_name = self
.attrs
.file_name
.clone()
.unwrap_or_else(|| format!("{}.pm", self.attrs.package_name.replace("::", "/")));
let path = std::path::Path::new(&file_name);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
@ -113,3 +122,85 @@ impl Package {
Ok(())
}
}
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))
}
}
}

View File

@ -1,13 +1,12 @@
#[perlmod::package(name = "RSPM::Foo", lib = "perlmod_test")]
mod export {
use failure::{bail, Error};
#[cfg(feature = "rust142")]
/// The fhe following requires rust 1.42 to work, as custom attributes on inline modules has only
/// been stabilized then.
mod pkg142;
#[export]
fn foo(a: u32, b: u32) -> Result<u32, Error> {
if a == 42 {
bail!("dying on magic number");
}
#[cfg(feature = "rustmuchlater")]
/// The following is what we ideally want to reach with future rust versions. It is technically
/// possible on nightly with #![feature(custom_inner_attributes)]
mod pkginline;
Ok(a + b)
}
}
/// This is possible on stable rust with some 1.3x already.
mod pkgstable;

View File

@ -0,0 +1,13 @@
#[perlmod::package(name = "RSPM::Foo", lib = "perlmod_test")]
mod export {
use failure::{bail, Error};
#[export]
fn foo(a: u32, b: u32) -> Result<u32, Error> {
if a == 42 {
bail!("dying on magic number");
}
Ok(a + b)
}
}

View File

@ -0,0 +1,12 @@
#![perlmod::package(name = "RSPM::Foo", lib = "perlmod_test")]
use failure::{bail, Error};
#[export]
fn foo(a: u32, b: u32) -> Result<u32, Error> {
if a == 42 {
bail!("dying on magic number");
}
Ok(a + b)
}

View File

@ -0,0 +1,26 @@
use failure::{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_bar as bar,
}
}

View File

@ -26,4 +26,4 @@ pub mod value;
pub use value::Value;
#[cfg(feature = "exporter")]
pub use perlmod_macro::package;
pub use perlmod_macro::{export, make_package, package};