Skip to content

Commit b981c45

Browse files
Merge pull request #94 from ElrondNetwork/abi-output-names
ABI generation - output names
2 parents 47053a1 + 15493c5 commit b981c45

File tree

10 files changed

+92
-38
lines changed

10 files changed

+92
-38
lines changed

contracts/feature-tests/abi-tester/abi_test_expected.abi.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
],
1919
"outputs": [
2020
{
21+
"name": "single output",
2122
"type": "AbiTestType"
2223
}
2324
]
@@ -27,12 +28,15 @@
2728
"inputs": [],
2829
"outputs": [
2930
{
31+
"name": "multi-result-1",
3032
"type": "i32"
3133
},
3234
{
35+
"name": "multi-result-2",
3336
"type": "[u8; 3]"
3437
},
3538
{
39+
"name": "multi-result-3",
3640
"type": "bytes"
3741
}
3842
]
@@ -42,9 +46,11 @@
4246
"inputs": [],
4347
"outputs": [
4448
{
49+
"name": "multi-too-few-1",
4550
"type": "i32"
4651
},
4752
{
53+
"name": "multi-too-few-2",
4854
"type": "[u8; 3]"
4955
},
5056
{

contracts/feature-tests/abi-tester/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,24 @@ use abi_test_type::*;
1414
#[elrond_wasm_derive::contract(AbiTesterImpl)]
1515
pub trait AbiTester {
1616
#[endpoint]
17+
#[output_name("single output")]
18+
#[output_name("this one doesn't show up")]
1719
fn echo_abi_test_type(&self, att: AbiTestType) -> AbiTestType {
1820
att
1921
}
2022

2123
#[endpoint]
24+
#[output_name("multi-result-1")]
25+
#[output_name("multi-result-2")]
26+
#[output_name("multi-result-3")]
27+
#[output_name("multi-result-in-excess")]
2228
fn multi_result_3(&self) -> MultiResult3<i32, [u8; 3], BoxedBytes> {
2329
(1, [2; 3], BoxedBytes::empty()).into()
2430
}
2531

2632
#[endpoint]
33+
#[output_name("multi-too-few-1")]
34+
#[output_name("multi-too-few-2")]
2735
fn multi_result_4(&self) -> MultiResult4<i32, [u8; 3], BoxedBytes, OnlyShowsUpAsNested3> {
2836
(
2937
1,

elrond-wasm-debug/src/abi_json/endpoint_abi_json.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ impl From<&InputAbi> for InputAbiJson {
3131

3232
#[derive(Serialize, Deserialize)]
3333
pub struct OutputAbiJson {
34+
#[serde(rename = "name")]
35+
#[serde(skip_serializing_if = "String::is_empty")]
36+
pub output_name: String,
3437
#[serde(rename = "type")]
3538
pub type_name: String,
3639
/// Bool that is only serialized when true
@@ -41,6 +44,7 @@ pub struct OutputAbiJson {
4144
impl From<&OutputAbi> for OutputAbiJson {
4245
fn from(abi: &OutputAbi) -> Self {
4346
OutputAbiJson {
47+
output_name: abi.output_name.into(),
4448
type_name: abi.type_name.clone(),
4549
multi_result: if abi.multi_result { Some(true) } else { None },
4650
}

elrond-wasm-derive/src/abi_gen.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ pub fn generate_abi_method_body(contract: &Contract) -> proc_macro2::TokenStream
3636
})
3737
.collect();
3838

39+
let output_names = &m.output_names;
3940
let output_snippet = match &m.return_type {
4041
syn::ReturnType::Default => quote! {},
4142
syn::ReturnType::Type(_, ty) => {
4243
let mut res_type = ty.clone();
4344
clear_all_type_lifetimes(&mut res_type);
4445
quote! {
45-
endpoint_abi.add_output::<#res_type>();
46+
endpoint_abi.add_output::<#res_type>(&[ #(#output_names),* ]);
4647
contract_abi.add_type_descriptions::<#res_type>();
4748
}
4849
},

elrond-wasm-derive/src/contract_gen_method.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ pub struct Method {
9999
pub name: syn::Ident,
100100
pub generics: syn::Generics,
101101
pub method_args: Vec<MethodArg>,
102+
pub output_names: Vec<String>,
102103
pub return_type: syn::ReturnType,
103104
pub body: Option<syn::Block>,
104105
}
@@ -327,12 +328,14 @@ impl Method {
327328
let metadata = extract_metadata(m);
328329
let allow_callback_args = matches!(metadata, MethodMetadata::Callback);
329330
let method_args = extract_method_args(m, is_payable(m), allow_callback_args);
331+
let output_names = find_output_names(m);
330332
Method {
331333
docs: extract_doc(m.attrs.as_slice()),
332334
metadata,
333335
name: m.sig.ident.clone(),
334336
generics: m.sig.generics.clone(),
335337
method_args,
338+
output_names,
336339
return_type: m.sig.output.clone(),
337340
body: m.default.clone(),
338341
}

elrond-wasm-derive/src/parse_attr.rs

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
static ATTR_PAYABLE: &str = "payable";
2+
static ATTR_OUTPUT_NAME: &str = "output_name";
23
static ATTR_PAYMENT: &str = "payment";
34
static ATTR_VAR_ARGS: &str = "var_args";
45
static ATTR_EVENT: &str = "event";
@@ -104,6 +105,37 @@ pub fn extract_doc(attrs: &[syn::Attribute]) -> Vec<String> {
104105
.collect()
105106
}
106107

108+
fn attr_one_string_arg(attr: &syn::Attribute) -> String {
109+
let result_str: String;
110+
let mut iter = attr.clone().tokens.into_iter();
111+
match iter.next() {
112+
Some(proc_macro2::TokenTree::Group(group)) => {
113+
if group.delimiter() != proc_macro2::Delimiter::Parenthesis {
114+
panic!("event paranthesis expected");
115+
}
116+
let mut iter2 = group.stream().into_iter();
117+
match iter2.next() {
118+
Some(proc_macro2::TokenTree::Literal(lit)) => {
119+
let str_val = lit.to_string();
120+
if !str_val.starts_with('\"') || !str_val.ends_with('\"') {
121+
panic!("string literal expected as attribute argument");
122+
}
123+
let substr = &str_val[1..str_val.len() - 1];
124+
result_str = substr.to_string();
125+
},
126+
_ => panic!("literal expected as event identifier"),
127+
}
128+
},
129+
_ => panic!("missing event identifier"),
130+
}
131+
132+
if iter.next().is_some() {
133+
panic!("event too many tokens in event attribute");
134+
}
135+
136+
result_str
137+
}
138+
107139
fn find_attr_one_string_arg(m: &syn::TraitItemMethod, attr_name: &str) -> Option<String> {
108140
let event_attr = m.attrs.iter().find(|attr| {
109141
if let Some(first_seg) = attr.path.segments.first() {
@@ -114,37 +146,22 @@ fn find_attr_one_string_arg(m: &syn::TraitItemMethod, attr_name: &str) -> Option
114146
});
115147
match event_attr {
116148
None => None,
117-
Some(attr) => {
118-
let result_str: String;
119-
let mut iter = attr.clone().tokens.into_iter();
120-
match iter.next() {
121-
Some(proc_macro2::TokenTree::Group(group)) => {
122-
if group.delimiter() != proc_macro2::Delimiter::Parenthesis {
123-
panic!("event paranthesis expected");
124-
}
125-
let mut iter2 = group.stream().into_iter();
126-
match iter2.next() {
127-
Some(proc_macro2::TokenTree::Literal(lit)) => {
128-
let str_val = lit.to_string();
129-
if !str_val.starts_with('\"') || !str_val.ends_with('\"') {
130-
panic!("string literal expected as attribute argument");
131-
}
132-
let substr = &str_val[1..str_val.len() - 1];
133-
result_str = substr.to_string();
134-
},
135-
_ => panic!("literal expected as event identifier"),
136-
}
137-
},
138-
_ => panic!("missing event identifier"),
139-
}
149+
Some(attr) => Some(attr_one_string_arg(attr)),
150+
}
151+
}
140152

141-
if iter.next().is_some() {
142-
panic!("event too many tokens in event attribute");
153+
pub fn find_output_names(m: &syn::TraitItemMethod) -> Vec<String> {
154+
m.attrs
155+
.iter()
156+
.filter(|attr| {
157+
if let Some(first_seg) = attr.path.segments.first() {
158+
first_seg.ident == ATTR_OUTPUT_NAME
159+
} else {
160+
false
143161
}
144-
145-
Some(result_str)
146-
},
147-
}
162+
})
163+
.map(attr_one_string_arg)
164+
.collect()
148165
}
149166

150167
pub struct EventAttribute {

elrond-wasm/src/abi/endpoint_abi.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub struct InputAbi {
1111

1212
#[derive(Clone, Debug)]
1313
pub struct OutputAbi {
14+
pub output_name: &'static str,
1415
pub type_name: String,
1516
pub multi_result: bool,
1617
}
@@ -33,7 +34,8 @@ impl EndpointAbi {
3334
});
3435
}
3536

36-
pub fn add_output<T: TypeAbi>(&mut self) {
37-
self.outputs.extend_from_slice(T::output_abis().as_slice());
37+
pub fn add_output<T: TypeAbi>(&mut self, output_names: &[&'static str]) {
38+
self.outputs
39+
.extend_from_slice(T::output_abis(output_names).as_slice());
3840
}
3941
}

elrond-wasm/src/abi/type_abi.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,17 @@ pub trait TypeAbi {
3535
/// however, MultiResultX when top-level can be seen as multiple endpoint results.
3636
/// This method gives it an opportunity to dissolve into its components.
3737
/// Should only be overridden by framework types.
38+
/// Output names are optionally provided in contracts via the `output_name` method attribute.
3839
#[doc(hidden)]
39-
fn output_abis() -> Vec<OutputAbi> {
40+
fn output_abis(output_names: &[&'static str]) -> Vec<OutputAbi> {
4041
let mut result = Vec::with_capacity(1);
42+
let output_name = if !output_names.is_empty() {
43+
output_names[0]
44+
} else {
45+
""
46+
};
4147
result.push(OutputAbi {
48+
output_name,
4249
type_name: Self::type_name(),
4350
multi_result: Self::is_multi_arg_or_result(),
4451
});
@@ -49,7 +56,7 @@ pub trait TypeAbi {
4956
impl TypeAbi for () {
5057
/// No another exception from the 1-type-1-output-abi rule:
5158
/// the unit type produces no output.
52-
fn output_abis() -> Vec<OutputAbi> {
59+
fn output_abis(_output_names: &[&'static str]) -> Vec<OutputAbi> {
5360
Vec::new()
5461
}
5562
}

elrond-wasm/src/types/multi_result.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,16 @@ macro_rules! multi_result_impls {
4747
true
4848
}
4949

50-
fn output_abis() -> Vec<OutputAbi> {
50+
fn output_abis(output_names: &[&'static str]) -> Vec<OutputAbi> {
5151
let mut result = Vec::new();
5252
$(
53-
result.append(&mut $name::output_abis());
53+
if output_names.len() > $n {
54+
result.append(&mut $name::output_abis(&[output_names[$n]]));
55+
56+
} else {
57+
result.append(&mut $name::output_abis(&[]));
58+
}
59+
5460
)+
5561
result
5662
}

elrond-wasm/src/types/sc_result.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,8 @@ impl<T: TypeAbi> TypeAbi for SCResult<T> {
7070
/// just like `()`.
7171
/// It is also possible to have `SCResult<MultiResultX<...>>`,
7272
/// so this gives the MultiResult to dissolve into its multiple output ABIs.
73-
fn output_abis() -> Vec<OutputAbi> {
74-
T::output_abis()
73+
fn output_abis(output_names: &[&'static str]) -> Vec<OutputAbi> {
74+
T::output_abis(output_names)
7575
}
7676

7777
fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {

0 commit comments

Comments
 (0)