mirror of
git://git.proxmox.com/git/perlmod.git
synced 2025-01-03 09:17:39 +03:00
macro: add serialize_error attribute
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
f381c740b4
commit
2aa0c70d0d
@ -128,6 +128,7 @@ pub struct FunctionAttrs {
|
||||
pub raw_return: bool,
|
||||
pub cv_variable: Option<Ident>,
|
||||
pub prototype: Option<String>,
|
||||
pub serialize_error: bool,
|
||||
}
|
||||
|
||||
impl TryFrom<AttributeArgs> for FunctionAttrs {
|
||||
@ -157,6 +158,8 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
|
||||
if path.is_ident("raw_return") {
|
||||
attrs.raw_return = true;
|
||||
} else if path.is_ident("serialize_error") {
|
||||
attrs.serialize_error = true;
|
||||
} else {
|
||||
error!(path => "unknown attribute");
|
||||
}
|
||||
|
@ -52,15 +52,20 @@ impl ArgumentAttrs {
|
||||
}
|
||||
}
|
||||
|
||||
enum Return {
|
||||
struct Return {
|
||||
result: bool,
|
||||
value: ReturnValue,
|
||||
}
|
||||
|
||||
enum ReturnValue {
|
||||
/// Return nothing. (This is different from returning an implicit undef!)
|
||||
None(bool),
|
||||
None,
|
||||
|
||||
/// Return a single element.
|
||||
Single(bool),
|
||||
Single,
|
||||
|
||||
/// We support tuple return types. They act like "list" return types in perl.
|
||||
Tuple(bool, usize),
|
||||
Tuple(usize),
|
||||
}
|
||||
|
||||
pub fn handle_function(
|
||||
@ -203,11 +208,23 @@ pub fn handle_function(
|
||||
}
|
||||
|
||||
let has_return_value = match &func.sig.output {
|
||||
syn::ReturnType::Default => Return::None(false),
|
||||
syn::ReturnType::Default => Return {
|
||||
result: false,
|
||||
value: ReturnValue::None,
|
||||
},
|
||||
syn::ReturnType::Type(_arrow, ty) => match get_result_type(ty) {
|
||||
(syn::Type::Tuple(tuple), result) if tuple.elems.is_empty() => Return::None(result),
|
||||
(syn::Type::Tuple(tuple), result) => Return::Tuple(result, tuple.elems.len()),
|
||||
(_, result) => Return::Single(result),
|
||||
(syn::Type::Tuple(tuple), result) if tuple.elems.is_empty() => Return {
|
||||
result,
|
||||
value: ReturnValue::None,
|
||||
},
|
||||
(syn::Type::Tuple(tuple), result) => Return {
|
||||
result,
|
||||
value: ReturnValue::Tuple(tuple.elems.len()),
|
||||
},
|
||||
(_, result) => Return {
|
||||
result,
|
||||
value: ReturnValue::Single,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -328,24 +345,43 @@ fn handle_return_kind(
|
||||
(quote! { _cv }, TokenStream::new())
|
||||
};
|
||||
|
||||
let return_error = if ret.result {
|
||||
if attr.serialize_error {
|
||||
quote! {
|
||||
match ::perlmod::to_value(&err) {
|
||||
Ok(err) => return Err(err.into_mortal().into_raw()),
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&format!("{}\n", err))
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
return Err(::perlmod::Value::new_string(&format!("{}\n", err))
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
let pthx = crate::pthx_param();
|
||||
match ret {
|
||||
Return::None(result) => {
|
||||
match ret.value {
|
||||
ReturnValue::None => {
|
||||
return_type = quote! { () };
|
||||
|
||||
if attr.raw_return {
|
||||
bail!(&attr.raw_return => "raw_return attribute is illegal without a return value");
|
||||
}
|
||||
|
||||
if result {
|
||||
if ret.result {
|
||||
handle_return = quote! {
|
||||
match #name(#passed_arguments) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&format!("{}\n", err))
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
Err(err) => { #return_error }
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -370,18 +406,14 @@ fn handle_return_kind(
|
||||
}
|
||||
};
|
||||
}
|
||||
Return::Single(result) => {
|
||||
ReturnValue::Single => {
|
||||
return_type = quote! { *mut ::perlmod::ffi::SV };
|
||||
|
||||
if result {
|
||||
if ret.result {
|
||||
handle_return = quote! {
|
||||
let result = match #name(#passed_arguments) {
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&format!("{}\n", err))
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
Err(err) => { #return_error }
|
||||
};
|
||||
};
|
||||
} else {
|
||||
@ -417,7 +449,7 @@ fn handle_return_kind(
|
||||
}
|
||||
};
|
||||
}
|
||||
Return::Tuple(result, count) => {
|
||||
ReturnValue::Tuple(count) => {
|
||||
return_type = {
|
||||
let mut rt = TokenStream::new();
|
||||
for _ in 0..count {
|
||||
@ -426,15 +458,11 @@ fn handle_return_kind(
|
||||
quote! { (#rt) }
|
||||
};
|
||||
|
||||
if result {
|
||||
if ret.result {
|
||||
handle_return = quote! {
|
||||
let result = match #name(#passed_arguments) {
|
||||
Ok(output) => output,
|
||||
Err(err) => {
|
||||
return Err(::perlmod::Value::new_string(&format!("{}\n", err))
|
||||
.into_mortal()
|
||||
.into_raw());
|
||||
}
|
||||
Err(err) => { #return_error }
|
||||
};
|
||||
};
|
||||
} else {
|
||||
|
@ -85,6 +85,24 @@ mod export {
|
||||
fn test_substr_return(#[raw] value: Value) -> Result<Value, Error> {
|
||||
Ok(value.substr(3..6)?)
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct MyError {
|
||||
a: String,
|
||||
b: String,
|
||||
}
|
||||
|
||||
#[export(serialize_error)]
|
||||
fn test_deserialized_error(fail: bool) -> Result<&'static str, MyError> {
|
||||
if fail {
|
||||
Err(MyError {
|
||||
a: "first".to_string(),
|
||||
b: "second".to_string(),
|
||||
})
|
||||
} else {
|
||||
Ok("worked")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[perlmod::package(name = "RSPM::EnvVarLibrary", lib = "x-${CARGO_PKG_NAME}-y")]
|
||||
|
9
test.pl
9
test.pl
@ -120,3 +120,12 @@ print("Substring test\n");
|
||||
my $orig = "OneTwoThree";
|
||||
my $sub = RSPM::Foo142::test_substr_return($orig);
|
||||
print("[$orig] [$sub]\n");
|
||||
|
||||
my $ok = RSPM::Foo142::test_deserialized_error(0);
|
||||
die "test_deserialized_error failed to return a value\n" if $ok ne 'worked';
|
||||
$ok = eval { RSPM::Foo142::test_deserialized_error(1) };
|
||||
die "test_deserialized_error error case returned a value\n" if defined $ok;
|
||||
my $err = $@;
|
||||
die "test_deserialized_error error is not a hash\n" if ref($err) ne 'HASH';
|
||||
die "structured error has invalid fields\n" if join(',', sort(keys(%$err))) ne 'a,b';
|
||||
print('error type: { a: ', $err->{a}, ', b: ', $err->{b}, " }\n");
|
||||
|
@ -45,3 +45,4 @@ Testing optional parameters
|
||||
3, None
|
||||
Substring test
|
||||
[OneTwoThree] [Two]
|
||||
error type: { a: first, b: second }
|
||||
|
Loading…
Reference in New Issue
Block a user