mirror of
git://git.proxmox.com/git/perlmod.git
synced 2025-01-19 18:03:36 +03:00
make_package macro implementation
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
b03c149435
commit
06a18771dd
@ -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,
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
13
perlmod-test/src/pkg142.rs
Normal file
13
perlmod-test/src/pkg142.rs
Normal 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)
|
||||
}
|
||||
}
|
12
perlmod-test/src/pkginline.rs
Normal file
12
perlmod-test/src/pkginline.rs
Normal 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)
|
||||
}
|
26
perlmod-test/src/pkgstable.rs
Normal file
26
perlmod-test/src/pkgstable.rs
Normal 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,
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
Loading…
x
Reference in New Issue
Block a user