Skip to content

Commit

Permalink
Have #[handle_result] support any Result type
Browse files Browse the repository at this point in the history
  • Loading branch information
uint committed Jul 20, 2023
1 parent 1f62a68 commit da2676b
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 25 deletions.
12 changes: 8 additions & 4 deletions near-sdk-macros/src/core_impl/abi/abi_generator.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote};
use syn::{Attribute, Lit::Str, Meta::NameValue, MetaNameValue, Type};
use syn::{parse_quote, Attribute, Lit::Str, Meta::NameValue, MetaNameValue, Type};

use crate::core_impl::{
utils, BindgenArgType, ImplItemMethodInfo, ItemImplInfo, MethodKind, ReturnKind, SerializerType,
Expand Down Expand Up @@ -202,7 +202,11 @@ impl ImplItemMethodInfo {
match &self.attr_signature_info.returns.kind {
Default => quote! { None },
General(ty) => self.abi_result_tokens_with_return_value(ty),
HandlesResult { ok_type } => self.abi_result_tokens_with_return_value(ok_type),
HandlesResult { ty } => {
// extract the `Ok` type from the result
let ty = parse_quote! { <#ty as near_sdk::__private::ResultTypeExt>::Okay };
self.abi_result_tokens_with_return_value(&ty)
}
}
}

Expand Down Expand Up @@ -329,7 +333,7 @@ mod tests {
callbacks: vec![],
callbacks_vec: None,
result: Some(near_sdk::__private::AbiType::Json {
type_schema: gen.subschema_for::<IsOk>(),
type_schema: gen.subschema_for::< <Result<IsOk, Error> as near_sdk::__private::ResultTypeExt>::Okay>(),
})
}
};
Expand Down Expand Up @@ -366,7 +370,7 @@ mod tests {
callbacks: vec![],
callbacks_vec: None,
result: Some(near_sdk::__private::AbiType::Borsh {
type_schema: <IsOk as near_sdk::borsh::BorshSchema>::schema_container(),
type_schema: < <Result<IsOk, Error> as near_sdk::__private::ResultTypeExt>::Okay as near_sdk::borsh::BorshSchema>::schema_container(),
})
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,6 @@ mod tests {
assert!(matches!(actual, ReturnType::Type(_, ty) if ty.as_ref() == &expected));
}

#[test]
fn handle_result_incorrect_return_type() {
let impl_type: Type = syn::parse_str("Hello").unwrap();
let mut method: ImplItemMethod = parse_quote! {
#[handle_result]
pub fn method(&self) -> &'static str { }
};
let actual = ImplItemMethodInfo::new(&mut method, false, impl_type).map(|_| ()).unwrap_err();
let expected = "Function marked with #[handle_result] should return Result<T, E> (where E implements FunctionError).";
assert_eq!(expected, actual.to_string());
}

#[test]
fn handle_result_without_marker() {
let impl_type: Type = syn::parse_str("Hello").unwrap();
Expand Down
2 changes: 1 addition & 1 deletion near-sdk-macros/src/core_impl/info_extractor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,5 @@ pub struct Returns {
pub enum ReturnKind {
Default,
General(Type),
HandlesResult { ok_type: Type },
HandlesResult { ty: Type },
}
9 changes: 1 addition & 8 deletions near-sdk-macros/src/core_impl/info_extractor/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,7 @@ fn is_view(sig: &Signature) -> bool {

fn parse_return_kind(typ: &Type, handles_result: bool) -> syn::Result<ReturnKind> {
if handles_result {
if let Some(ok_type) = utils::extract_ok_type(typ) {
Ok(ReturnKind::HandlesResult { ok_type: ok_type.clone() })
} else {
Err(Error::new(
typ.span(),
"Function marked with #[handle_result] should return Result<T, E> (where E implements FunctionError).",
))
}
Ok(ReturnKind::HandlesResult { ty: typ.clone() })
} else if utils::type_is_result(typ) {
Err(Error::new(
typ.span(),
Expand Down
1 change: 1 addition & 0 deletions near-sdk/compilation_tests/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ fn compilation_tests() {
t.compile_fail("compilation_tests/generic_function.rs");
t.compile_fail("compilation_tests/generic_const_function.rs");
t.pass("compilation_tests/self_support.rs");
t.pass("compilation_tests/handle_result_alias.rs");
// The following couple tests should be activated before releasing 5.0
// See: https://github.com/near/near-sdk-rs/issues/1005
//
Expand Down
20 changes: 20 additions & 0 deletions near-sdk/compilation_tests/handle_result_alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use borsh::{BorshDeserialize, BorshSerialize};
use near_sdk::near_bindgen;

type MyResult = Result<u32, &'static str>;

#[near_bindgen]
#[derive(Default, BorshDeserialize, BorshSerialize)]
struct Contract {
value: u32,
}

#[near_bindgen]
impl Contract {
#[handle_result]
pub fn fun(&self) -> MyResult {
Err("error")
}
}

fn main() {}
3 changes: 3 additions & 0 deletions near-sdk/src/private/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub use schemars;
mod metadata;
pub use metadata::{Metadata, MethodMetadata};

mod result_type_ext;
pub use result_type_ext::ResultTypeExt;

use crate::IntoStorageKey;
use borsh::BorshSerialize;

Expand Down
17 changes: 17 additions & 0 deletions near-sdk/src/private/result_type_ext.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
pub trait ResultTypeExt: seal::ResultTypeExtSeal {
type Okay;
type Error;
}

impl<T, E> ResultTypeExt for Result<T, E> {
type Okay = T;
type Error = E;
}

// This is the "sealed trait" pattern:
// https://rust-lang.github.io/api-guidelines/future-proofing.html
mod seal {
pub trait ResultTypeExtSeal {}

impl<T, E> ResultTypeExtSeal for Result<T, E> {}
}

0 comments on commit da2676b

Please sign in to comment.