1#[macro_use]
8extern crate quote;
9
10use proc_macro::TokenStream;
12use proc_macro2::Span;
13use quote::{quote, ToTokens};
14
15use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Item, Meta, NestedMeta};
16
17#[allow(dead_code)]
18fn attach_error<A>(mut v: syn::Result<A>, msg: &str) -> syn::Result<A> {
19 if let Err(e) = v.as_mut() {
20 let span = e.span();
21 e.combine(syn::Error::new(span, msg));
22 }
23 v
24}
25
26#[allow(dead_code)]
27fn get_attribute_value<'a, I: IntoIterator<Item = &'a Meta>>(
28 iter: I,
29 name: &str,
30) -> syn::Result<Option<&'a syn::LitStr>> {
31 for attr in iter.into_iter() {
32 match attr {
33 Meta::NameValue(mnv) => {
34 if mnv.path.is_ident(name) {
35 if let syn::Lit::Str(lit) = &mnv.lit {
36 return Ok(Some(lit));
37 } else {
38 return Err(syn::Error::new(
39 mnv.span(),
40 format!("The `{}` attribute must be a string literal.", name),
41 ));
42 }
43 }
44 }
45 Meta::Path(p) => {
46 if p.is_ident(name) {
47 return Err(syn::Error::new(
48 attr.span(),
49 format!("The `{}` attribute must have a string literal value.", name),
50 ));
51 }
52 }
53 Meta::List(p) => {
54 if p.path.is_ident(name) {
55 return Err(syn::Error::new(
56 attr.span(),
57 format!("The `{}` attribute must have a string literal value.", name),
58 ));
59 }
60 }
61 }
62 }
63 Ok(None)
64}
65
66#[allow(dead_code)]
67fn contains_attribute<'a, I: IntoIterator<Item = &'a Meta>>(iter: I, name: &str) -> bool {
68 iter.into_iter().any(|attr| attr.path().is_ident(name))
69}
70
71#[allow(dead_code)]
72fn contract_function_optional_args_tokens<'a, I: Copy + IntoIterator<Item = &'a Meta>>(
73 attrs: I,
74 amount_ident: &syn::Ident,
75 required_args: &mut Vec<&str>,
76) -> (proc_macro2::TokenStream, Vec<proc_macro2::TokenStream>) {
77 let mut setup_fn_args = proc_macro2::TokenStream::new();
78 let mut fn_args = vec![];
79 if contains_attribute(attrs, "payable") {
80 required_args.push("amount: Amount");
81 fn_args.push(quote!(#amount_ident));
82 } else {
83 setup_fn_args.extend(quote! {
84 if #amount_ident != 0 {
85 return -1;
86 }
87 });
88 };
89
90 if contains_attribute(attrs, "enable_logger") {
91 required_args.push("logger: &mut impl HasLogger");
92 let logger_ident = format_ident!("logger");
93 setup_fn_args.extend(quote!(let mut #logger_ident = xq_std::Logger::init();));
94 fn_args.push(quote!(&mut #logger_ident));
95 }
96 (setup_fn_args, fn_args)
97}
98
99fn get_attribute(metas: Vec<NestedMeta>, value: &str) -> syn::Result<Option<syn::LitStr>> {
100 for item in metas {
101 match item {
102 NestedMeta::Meta(meta) => match meta {
103 Meta::NameValue(nv) => {
104 if nv.path.is_ident(value) {
105 if let syn::Lit::Str(lit) = nv.lit {
106 return Ok(Some(lit));
107 } else {
108 return Err(syn::Error::new(
109 nv.span(),
110 format!("the attribute must be a string literal."),
111 ));
112 }
113 }
114 }
115 Meta::Path(_) => {
116 return Err(syn::Error::new(
117 meta.span(),
118 format!("the attribute must be a string literal."),
119 ))
120 }
121 Meta::List(_) => {
122 return Err(syn::Error::new(
123 meta.span(),
124 format!("the attribute must be a string literal."),
125 ))
126 }
127 },
128 NestedMeta::Lit(_) => {
129 return Err(syn::Error::new(
130 item.span(),
131 format!("the attribute must be a string literal."),
132 ))
133 }
134 }
135 }
136 return Ok(None);
137}
138
139fn contains_attribute2(metas: Vec<NestedMeta>, value: &str) -> bool {
140 for attr in metas.iter() {
141 match attr {
142 NestedMeta::Meta(meta) => {
143 if meta.path().is_ident(value) {
144 return true;
145 }
146 }
147 NestedMeta::Lit(_) => {}
148 }
149 }
150 false
152}
153
154#[proc_macro_attribute]
155pub fn init(attr: TokenStream, item: TokenStream) -> TokenStream {
156 let attrs = parse_macro_input!(attr as AttributeArgs);
157 let contract = get_attribute(attrs.clone(), "contract").unwrap().unwrap();
158 let mut setup_function_args = proc_macro2::TokenStream::new();
161 let mut function_args = vec![];
162 let amount_ident = format_ident!("amount");
163
164 if contains_attribute2(attrs.clone(), "payable") {
165 function_args.push(quote!(#amount_ident));
166 } else {
167 setup_function_args.extend(quote! {
168 if #amount_ident != 0 {
169 return -1;
170 }
171 });
172 };
173
174 let ast = parse_macro_input!(item as Item);
175 let init_function_name = format_ident!("init_{}", contract.value());
176 let init_name = format!("init_{}", contract.value());
177 let function_name = if let syn::Item::Fn(itemfn) = ast.clone() {
180 itemfn.sig.ident
181 } else {
182 return syn::Error::new(Span::call_site(), format!("#[init] must be function."))
183 .into_compile_error()
184 .into();
185 };
186 let output = quote! {
187 #ast
188 #[export_name = #init_name]
189 pub extern "C" fn #init_function_name(amount:u64)->i32{
190 use xq_std::{ContractContext,serde_json};
191 let initctx = ContractContext;
192 #setup_function_args
193 match #function_name(initctx, #(#function_args),*){
194 Ok(o)=>{
195 let r = serde_json::to_string(&o).unwrap();
196 ContractContext.return_data(r.clone());
197 return 1
198 }
199 Err(e)=>{
200 let err = e.to_string();
201 ContractContext.error(err.clone());
202 return 0
203 }
204 }
205 }
206 };
207
208 TokenStream::from(output)
209}
210
211#[proc_macro_attribute]
212pub fn call(attr: TokenStream, item: TokenStream) -> TokenStream {
213 let attrs = parse_macro_input!(attr as AttributeArgs);
214 let contract = get_attribute(attrs.clone(), "contract").unwrap().unwrap();
215 let function = get_attribute(attrs.clone(), "function").unwrap().unwrap();
216 let mut setup_function_args = proc_macro2::TokenStream::new();
219 let mut function_args = vec![];
220 let amount_ident = format_ident!("amount");
221
222 if contains_attribute2(attrs.clone(), "payable") {
223 function_args.push(quote!(#amount_ident));
224 } else {
225 setup_function_args.extend(quote! {
226 if #amount_ident != 0 {
227 return -1;
228 }
229 });
230 };
231
232 let ast = parse_macro_input!(item as Item);
233 let call_function_name = format_ident!("{}_{}", contract.value(), function.value());
234 let function_name = if let syn::Item::Fn(itemfn) = ast.clone() {
238 itemfn.sig.ident
239 } else {
240 return syn::Error::new(Span::call_site(), format!("#[call] must be function."))
241 .into_compile_error()
242 .into();
243 };
244
245 let output = quote! {
246 #ast
247 #[no_mangle]
248 pub extern "C" fn #call_function_name(amount:u64)->i32{
249
250 use xq_std::{ContractContext, serde_json};
251 let initctx = ContractContext;
252 #setup_function_args
254 match #function_name(initctx, #(#function_args),*){
255 Ok(o)=>{
256 let r = serde_json::to_string(&o).unwrap();
257 ContractContext.return_data(r.clone());
258 return 1
259 }
260 Err(e)=>{
261 let err = e.to_string();
262 ContractContext.error(err.clone());
263 return 0
264 }
265 }
266
267 }
268 };
269
270 TokenStream::from(output)
271}
272
273#[proc_macro_derive(Output)]
274pub fn output_derive(input: TokenStream) -> TokenStream {
275 let ast: syn::DeriveInput = syn::parse(input.clone()).unwrap();
276 let enum_ident = ast.clone().ident;
277 let enum_data = match &ast.data {
278 syn::Data::Enum(data) => data,
279 _ => {
280 return syn::Error::new(ast.span(), "Output can only be derived for enums.")
281 .to_compile_error()
282 .into()
283 }
284 };
285 let mut field_fmt = vec![];
286 let mut field_string = vec![];
287 for field in &enum_data.variants {
288 field_fmt.push(field.ident.clone());
289 field_string.push(field.ident.to_string());
290 }
291
292 let display = quote! {
293 impl core::fmt::Display for #enum_ident{
294 fn fmt(&self, f:&mut core::fmt::Formatter) -> core::fmt::Result{
295 match self{
298 #(#enum_ident::#field_fmt => core::write!(f, "{}", #field_string),)*
299 }
300 }
301 }
302 };
303
304 let ge = quote! {
305 #display
306 };
307 return ge.into();
308}
309
310#[proc_macro_attribute]
311pub fn state(_attr: TokenStream, item: TokenStream) -> TokenStream {
312 let mut output = proc_macro2::TokenStream::new();
313 let data_ident = if let Ok(ast) = syn::parse::<syn::ItemStruct>(item.clone()) {
314 ast.to_tokens(&mut output);
315 ast.ident
316 } else {
317 return syn::Error::new_spanned(
318 proc_macro2::TokenStream::from(item),
319 "#[state] only supports structs.",
320 )
321 .to_compile_error()
322 .into();
323 };
324
325 let impl_state = quote! {
326 impl #data_ident {
327 fn contract_state_set(&mut self,){
328 return
329 }
330
331 fn contract_state_get(&self){
332 return
333 }
334 }
335
336 };
337
338 impl_state.to_tokens(&mut output);
339
340 output.into()
341}