mirror of
git://git.proxmox.com/git/perlmod.git
synced 2025-03-13 04:58:16 +03:00
introduce try_from_ref argument attribute
and document attributes in the #[export] macro documentation Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
63af6eebc9
commit
7de9fdf07b
@ -13,6 +13,36 @@ pub struct XSub {
|
||||
pub tokens: TokenStream,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct ArgumentAttrs {
|
||||
raw: bool,
|
||||
try_from_ref: bool,
|
||||
}
|
||||
|
||||
impl ArgumentAttrs {
|
||||
fn handle_path(&mut self, path: &syn::Path) -> bool {
|
||||
if path.is_ident("raw") {
|
||||
self.raw = true;
|
||||
} else if path.is_ident("try_from_ref") {
|
||||
self.try_from_ref = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
fn validate(&self, span: Span) -> Result<(), Error> {
|
||||
if self.raw && self.try_from_ref {
|
||||
bail!(
|
||||
span,
|
||||
"`raw` and `try_from_ref` attributes are mutually exclusive"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_function(
|
||||
attr: FunctionAttrs,
|
||||
mut func: syn::ItemFn,
|
||||
@ -37,19 +67,15 @@ pub fn handle_function(
|
||||
let mut deserialized_arguments = TokenStream::new();
|
||||
let mut passed_arguments = TokenStream::new();
|
||||
for arg in &mut func.sig.inputs {
|
||||
let mut raw_arg = false;
|
||||
let mut argument_attrs = ArgumentAttrs::default();
|
||||
|
||||
let pat_ty = match arg {
|
||||
syn::FnArg::Receiver(_) => bail!(arg => "cannot export self-taking methods as xsubs"),
|
||||
syn::FnArg::Typed(ref mut pt) => {
|
||||
pt.attrs.retain(|attr| {
|
||||
if attr.path.is_ident("raw") {
|
||||
raw_arg = true;
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
pt.attrs
|
||||
.retain(|attr| !argument_attrs.handle_path(&attr.path));
|
||||
use syn::spanned::Spanned;
|
||||
argument_attrs.validate(pt.span())?;
|
||||
&*pt
|
||||
}
|
||||
};
|
||||
@ -86,10 +112,22 @@ pub fn handle_function(
|
||||
};
|
||||
});
|
||||
|
||||
if raw_arg {
|
||||
if argument_attrs.raw {
|
||||
deserialized_arguments.extend(quote! {
|
||||
let #deserialized_name = #extracted_name;
|
||||
});
|
||||
} else if argument_attrs.try_from_ref {
|
||||
deserialized_arguments.extend(quote! {
|
||||
let #deserialized_name: #arg_type =
|
||||
match ::std::convert::TryFrom::try_from(&#extracted_name) {
|
||||
Ok(arg) => arg,
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&err.to_string())
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
};
|
||||
});
|
||||
} else {
|
||||
deserialized_arguments.extend(quote! {
|
||||
let #deserialized_name: #arg_type =
|
||||
|
@ -42,4 +42,23 @@ pub use value::Value;
|
||||
|
||||
#[cfg(feature = "exporter")]
|
||||
#[doc(inline)]
|
||||
pub use perlmod_macro::{export, package};
|
||||
pub use perlmod_macro::package;
|
||||
|
||||
#[cfg(feature = "exporter")]
|
||||
#[doc(inline)]
|
||||
/// Attribute to export a function so that it can be installed as an `xsub` in perl. See the
|
||||
/// [`package!`](macro@package) macro for a usage example.
|
||||
///
|
||||
/// This macro can optionally take a `raw_return` argument specifying that the return type, which
|
||||
/// must be a [`Value`], will be returned as is, and not go through serialization.
|
||||
///
|
||||
/// Additionally, function parameters can also use the following attributes:
|
||||
///
|
||||
/// * `#[raw]` with a parameter of type [`Value`]: The parameter will be passed as
|
||||
/// is and not go through deserialization.
|
||||
/// * `#[try_from_ref]`: Instead of regular deserialization, `TryFrom::try_from(&Value)` will be
|
||||
/// used.
|
||||
///
|
||||
/// Implementing the `TryFrom` trait accordingly can make using blessed references more
|
||||
/// convenient, but at the cost of hiding underlying `unsafe` code.
|
||||
pub use perlmod_macro::export;
|
||||
|
@ -232,7 +232,7 @@ impl Value {
|
||||
///
|
||||
/// This is mainly a helper to be used for blessed values. This only checks that the value
|
||||
/// itself is any kind of reference, then assumes it contains something resembling a pointer
|
||||
/// (see [`Value::pv_raw`]), and if so, simply casts it to `T`.
|
||||
/// (see [`ScalarRef::pv_raw`](ScalarRef::pv_raw())), and if so, simply casts it to `T`.
|
||||
pub unsafe fn from_ref_box<T>(&self) -> Result<&T, Error> {
|
||||
let ptr = self
|
||||
.dereference()
|
||||
@ -246,9 +246,9 @@ impl Value {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// See [`Value::from_ref_box`]. This additionally uses [`Value::reftype`] to check that the
|
||||
/// passed value was indeed blessed into the provided `package` name. Other than that, it
|
||||
/// cannot verify the the contained pointer is truly a `T`.
|
||||
/// See [`Value::from_ref_box`]. This additionally uses [`reftype`](ScalarRef::reftype) to
|
||||
/// check that the passed value was indeed blessed into the provided `package` name. Other than
|
||||
/// that, it cannot verify the the contained pointer is truly a `T`.
|
||||
pub unsafe fn from_blessed_box<'a, T>(&'a self, package: &'_ str) -> Result<&'a T, Error> {
|
||||
let ptr = self
|
||||
.dereference()
|
||||
|
Loading…
x
Reference in New Issue
Block a user