1#![feature(box_into_inner)]
2extern crate proc_macro;
3
4#[cfg(test)]
5mod function_tests;
6
7use convert_case::{Case::Camel, Casing};
8use fancy_regex::Regex;
9use proc_macro::TokenStream;
10use proc_macro2::{Ident, Span};
11use proc_macro_error::{abort, abort_call_site, proc_macro_error};
12use quote::quote;
13use serde_derive::Deserialize;
14use tiny_keccak::{Hasher, Keccak};
15
16type MayString = Option<String>;
17
18#[derive(Deserialize)]
19#[serde(rename_all = "camelCase")]
20#[allow(dead_code)]
21struct AbiIO {
22 #[serde(skip_serializing_if = "Option::is_none")]
23 internal_type: Option<String>,
24 #[serde(skip_serializing_if = "Option::is_none")]
25 name: MayString,
26 r#type: String,
27}
28
29#[derive(Debug, PartialEq)]
31enum ContractMode {
32 DefaultMode,
36 RustyMode,
39 AutoMode,
43}
44
45#[allow(dead_code)]
47#[derive(Debug, PartialEq)]
48enum ContractOption {
49 DefaultMessage(String),
51}
52
53fn parse_contract_mode_and_options(
54 attr: String,
55) -> Result<(ContractMode, Vec<ContractOption>), &'static str> {
56 let (arg_str, option_str) = match attr.split_once(',') {
57 Some((head, tail)) => (head, tail),
58 None => {
59 if attr.starts_with("default") {
60 ("", attr.as_str())
61 } else {
62 (attr.as_str(), "")
63 }
64 }
65 };
66 let options = if let Ok(Some(cap)) =
67 unsafe { Regex::new(r#"default.*=.*"(?<default>[^"]*)""#).unwrap_unchecked() }
68 .captures(option_str)
69 {
70 let default = unsafe { cap.name("default").unwrap_unchecked() }.as_str();
71 vec![ContractOption::DefaultMessage(default.into())]
72 } else {
73 vec![]
74 };
75 let output = match arg_str {
76 "auto" => (ContractMode::AutoMode, options),
77 "rusty" => {
78 if options.len() != 0 {
79 return Err("can not set default message for rusty mode");
80 }
81 (ContractMode::RustyMode, options)
82 }
83 _ => (ContractMode::DefaultMode, options),
84 };
85 Ok(output)
86}
87
88fn get_function_signature(function_prototype: &str) -> [u8; 4] {
89 let mut sig = [0; 4];
90 let mut hasher = Keccak::v256();
91 hasher.update(function_prototype.as_bytes());
92 hasher.finalize(&mut sig);
93 sig
94}
95
96fn write_function_signature(sig_str: &str) -> String {
97 let re = unsafe { Regex::new(r"^(?P<name>[^(]+?)\((?P<params>[^)]*?)\)").unwrap_unchecked() };
98 if let Ok(Some(cap)) = re.captures(sig_str) {
99 let fn_name = unsafe { cap.name("name").unwrap_unchecked() }.as_str();
100 let params = unsafe { cap.name("params").unwrap_unchecked() }
101 .as_str()
102 .replace(" ", "");
103 let canonical_fn = format!(
104 "{}({})",
105 fn_name,
106 params
107 .split(',')
108 .map(|p| {
109 let p_split = p.split(':').collect::<Vec<_>>();
110 if p_split.len() == 2 {
111 p_split[1]
112 } else {
113 p_split[0]
114 }
115 .trim()
116 })
117 .collect::<Vec<_>>()
118 .join(",")
119 );
120 format!(r"{:?}", get_function_signature(&canonical_fn))
121 } else {
122 format!(
123 "{}_SIG",
124 sig_str.to_string().replace(" ", "").to_ascii_uppercase()
125 )
126 }
127}
128
129#[proc_macro_error]
186#[proc_macro_attribute]
187pub fn ewasm_main(attr: TokenStream, item: TokenStream) -> TokenStream {
188 let input = syn::parse_macro_input!(item as syn::ItemFn);
189 let name = &input.sig.ident;
190 if !input.sig.inputs.is_empty() {
191 abort!(
192 input.sig.inputs,
193 "ewasm_main only wrap the function without inputs"
194 )
195 }
196
197 let output_type = match input.sig.clone().output {
198 syn::ReturnType::Type(_, boxed) => match Box::into_inner(boxed) {
199 syn::Type::Path(syn::TypePath { path: p, .. }) => {
200 let mut ok_type: Option<String> = None;
201 let mut segments = p.segments;
202 while let Some(pair) = segments.pop() {
203 ok_type = match pair.into_value() {
204 syn::PathSegment {
205 arguments:
206 syn::PathArguments::AngleBracketed(
207 syn::AngleBracketedGenericArguments { args: a, .. },
208 ),
209 ..
210 } => match a.first() {
211 Some(syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
212 path: p,
213 ..
214 }))) => {
215 if let Some(syn::PathSegment { ident: i, .. }) = p.segments.last() {
216 Some(i.to_string())
217 } else {
218 None
219 }
220 }
221 _ => None,
222 },
223 _ => None,
224 };
225 if ok_type.is_some() {
226 break;
227 }
228 }
229 ok_type
230 }
231 _ => None,
232 },
233 _ => None,
234 };
235
236 let (contract_mode, options) = match parse_contract_mode_and_options(attr.to_string()) {
237 Ok(o) => o,
238 Err(e) => abort_call_site!(e),
239 };
240
241 let mut default_message = None;
242 for option in options.into_iter() {
243 let ContractOption::DefaultMessage(s) = option;
244 default_message = Some(s);
245 }
246
247 match contract_mode {
248 ContractMode::AutoMode if Some("EwasmAny".to_string()) == output_type && default_message.is_some() => quote! {
249 #[cfg(target_arch = "wasm32")]
250 use sewup::bincode;
251 #[cfg(target_arch = "wasm32")]
252 use sewup::ewasm_api::finish_data;
253 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
254 pub fn main() {}
255 #[cfg(target_arch = "wasm32")]
256 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
257 #[no_mangle]
258 pub fn main() {
259 #input
260 match #name() {
261 Ok(r) => {
262 finish_data(&r.bin);
263 },
264 Err(e) => {
265 finish_data(&#default_message.as_bytes());
266 }
267 }
268 }
269 },
270 ContractMode::AutoMode if Some("EwasmAny".to_string()) == output_type => quote! {
271 #[cfg(target_arch = "wasm32")]
272 use sewup::bincode;
273 #[cfg(target_arch = "wasm32")]
274 use sewup::ewasm_api::finish_data;
275 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
276 pub fn main() {}
277 #[cfg(target_arch = "wasm32")]
278 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
279 #[no_mangle]
280 pub fn main() {
281 #input
282 match #name() {
283 Ok(r) => {
284 finish_data(&r.bin);
285 },
286 Err(e) => {
287 let error_msg = e.to_string();
288 finish_data(&error_msg.as_bytes());
289 }
290 }
291 }
292 },
293 ContractMode::AutoMode if default_message.is_some() => quote! {
294 #[cfg(target_arch = "wasm32")]
295 use sewup::bincode;
296 #[cfg(target_arch = "wasm32")]
297 use sewup::ewasm_api::finish_data;
298 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
299 pub fn main() {}
300 #[cfg(target_arch = "wasm32")]
301 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
302 #[no_mangle]
303 pub fn main() {
304 #input
305 match #name() {
306 Ok(r) => {
307 let bin = bincode::serialize(&r).expect("The resuslt of `ewasm_main` should be serializable");
308 finish_data(&bin);
309 },
310 Err(_) => {
311 finish_data(&#default_message.as_bytes());
312 }
313 }
314 }
315 },
316 ContractMode::AutoMode => quote! {
317 #[cfg(target_arch = "wasm32")]
318 use sewup::bincode;
319 #[cfg(target_arch = "wasm32")]
320 use sewup::ewasm_api::finish_data;
321 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
322 pub fn main() {}
323 #[cfg(target_arch = "wasm32")]
324 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
325 #[no_mangle]
326 pub fn main() {
327 #input
328 match #name() {
329 Ok(r) => {
330 let bin = bincode::serialize(&r).expect("The resuslt of `ewasm_main` should be serializable");
331 finish_data(&bin);
332 },
333 Err(e) => {
334 let error_msg = e.to_string();
335 finish_data(&error_msg.as_bytes());
336 }
337 }
338 }
339 },
340 ContractMode::RustyMode if Some("EwasmAny".to_string()) == output_type => quote! {
341 #[cfg(target_arch = "wasm32")]
342 use sewup::bincode;
343 #[cfg(target_arch = "wasm32")]
344 use sewup::ewasm_api::finish_data;
345 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
346 pub fn main() {}
347 #[cfg(target_arch = "wasm32")]
348 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
349 #[no_mangle]
350 pub fn main() {
351 #input
352 let r = #name().map(|any| any.bin);
353 let bin = bincode::serialize(&r).expect("The resuslt of `ewasm_main` should be serializable");
354 finish_data(&bin);
355 }
356 },
357 ContractMode::RustyMode => quote! {
358 #[cfg(target_arch = "wasm32")]
359 use sewup::bincode;
360 #[cfg(target_arch = "wasm32")]
361 use sewup::ewasm_api::finish_data;
362 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
363 pub fn main() {}
364 #[cfg(target_arch = "wasm32")]
365 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
366 #[no_mangle]
367 pub fn main() {
368 #input
369 let r = #name();
370 let bin = bincode::serialize(&r).expect("The resuslt of `ewasm_main` should be serializable");
371 finish_data(&bin);
372 }
373 },
374 ContractMode::DefaultMode if default_message.is_some() => quote! {
375 #[cfg(target_arch = "wasm32")]
376 use sewup::bincode;
377 #[cfg(target_arch = "wasm32")]
378 use sewup::ewasm_api::finish_data;
379 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
380 pub fn main() {}
381 #[cfg(target_arch = "wasm32")]
382 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
383 #[no_mangle]
384 pub fn main() {
385 #input
386 if let Err(e) = #name() {
387 finish_data(#default_message.as_bytes());
388 }
389 }
390 },
391 ContractMode::DefaultMode => quote! {
392 #[cfg(target_arch = "wasm32")]
393 use sewup::bincode;
394 #[cfg(target_arch = "wasm32")]
395 use sewup::ewasm_api::finish_data;
396 #[cfg(all(not(target_arch = "wasm32"), not(test)))]
397 pub fn main() {}
398 #[cfg(target_arch = "wasm32")]
399 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
400 #[no_mangle]
401 pub fn main() {
402 #input
403 if let Err(e) = #name() {
404 let error_msg = e.to_string();
405 finish_data(&error_msg.as_bytes());
406 }
407 }
408 }
409 }.into()
410}
411
412fn parse_fn_attr(
413 fn_name: String,
414 attr: String,
415) -> Result<(Option<String>, String, Option<String>), &'static str> {
416 let attr_str = attr.replace(" ", "").replace("\n", "");
417 return if attr_str.is_empty() {
418 Ok((None, "{}".into(), None))
419 } else if let Some((head, tail)) = attr_str.split_once(',') {
420 if tail.is_empty() {
421 Ok((Some(head.replace("\"", "")), "{}".into(), None))
422 } else {
423 let root_str = if let Ok(Some(cap)) =
424 unsafe { Regex::new(r"only_by=(?P<account>[^,]*)").unwrap_unchecked() }
425 .captures(&attr_str)
426 {
427 Some(
428 unsafe { cap.name("account").unwrap_unchecked() }
429 .as_str()
430 .into(),
431 )
432 } else {
433 None
434 };
435
436 let mut json = "{".to_string();
437 if let Ok(Some(cap)) =
438 unsafe { Regex::new(r"constant=(?P<constant>[^,]*)").unwrap_unchecked() }
439 .captures(&attr_str)
440 {
441 match unsafe { cap.name("constant").unwrap_unchecked() }.as_str() {
442 "true" => json.push_str(r#""constant":true,"#),
443 "false" => json.push_str(r#""constant":false,"#),
444 _ => return Err("constacnt should be true or false"),
445 }
446 } else {
447 json.push_str(r#""constant":false,"#)
448 }
449
450 if let Ok(Some(cap)) =
451 unsafe { Regex::new(r#"inputs=(?<inputs>\[.*?\])(?!")"#).unwrap_unchecked() }
452 .captures(&attr_str)
453 {
454 json.push_str(r#""inputs":"#);
455 let inputs_str = unsafe { cap.name("inputs").unwrap_unchecked() }.as_str();
456 for cap in Regex::new(r"\{[^\{\}]*\}").unwrap().find_iter(inputs_str) {
457 if let Ok(input_cap) = cap {
458 if serde_json::from_str::<AbiIO>(input_cap.as_str()).is_err() {
459 return Err("inputs are not valid format");
460 }
461 }
462 }
463 json.push_str(inputs_str);
464 json.push(',');
465 } else {
466 json.push_str(r#""inputs":[],"#);
467 }
468
469 if let Ok(Some(cap)) = unsafe { Regex::new(r"name=(?P<name>[^,]*)").unwrap_unchecked() }
470 .captures(&attr_str)
471 {
472 json.push_str(r#""name":""#);
473 json.push_str(unsafe { cap.name("name").unwrap_unchecked() }.as_str());
474 json.push_str(r#"","#);
475 } else {
476 json.push_str(&format!(r#""name":"{}","#, fn_name.to_case(Camel)));
477 }
478
479 if let Ok(Some(cap)) =
480 unsafe { Regex::new(r#"outputs=(?<outputs>\[.*?\])(?!")"#).unwrap_unchecked() }
481 .captures(&attr_str)
482 {
483 json.push_str(r#""outputs":"#);
484 let outputs_str = unsafe { cap.name("outputs").unwrap_unchecked() }.as_str();
485 for cap in Regex::new(r"\{[^\{\}]*\}").unwrap().find_iter(outputs_str) {
486 if let Ok(output_cap) = cap {
487 if serde_json::from_str::<AbiIO>(output_cap.as_str()).is_err() {
488 return Err("outputs are not valid format");
489 }
490 }
491 }
492 json.push_str(outputs_str);
493 json.push(',');
494 } else {
495 json.push_str(r#""outputs":[],"#);
496 }
497
498 if let Ok(Some(cap)) =
499 unsafe { Regex::new(r"payable=(?P<payable>[^,]*)").unwrap_unchecked() }
500 .captures(&attr_str)
501 {
502 match unsafe { cap.name("payable").unwrap_unchecked() }.as_str() {
503 "true" => json.push_str(r#""payable":true,"#),
504 "false" => json.push_str(r#""payable":false,"#),
505 _ => return Err("payable should be true or false"),
506 }
507 } else {
508 json.push_str(r#""payable":false,"#);
509 }
510
511 if let Ok(Some(cap)) = unsafe {
512 Regex::new(r"stateMutability=(?P<stateMutability>[^,]*)").unwrap_unchecked()
513 }
514 .captures(&attr_str)
515 {
516 match unsafe { cap.name("stateMutability").unwrap_unchecked() }.as_str() {
517 "nonpayable" => json.push_str(r#""stateMutability":"nonpayable","#),
518 "view" => json.push_str(r#""stateMutability":"view","#),
519 _ => return Err("stateMutability should be nonpayable or view"),
520 }
521 } else {
522 json.push_str(r#""stateMutability":"view","#);
523 }
524
525 json.push_str(r#""type":"function"}"#);
526 if head.starts_with("only_by") {
527 Ok((None, json, root_str))
528 } else {
529 Ok((Some(head.replace("\"", "")), json, root_str))
530 }
531 }
532 } else if let Ok(Some(cap)) =
533 unsafe { Regex::new(r"only_by=(?P<account>[^,]*)").unwrap_unchecked() }.captures(&attr_str)
534 {
535 Ok((
536 None,
537 "{}".into(),
538 Some(
539 unsafe { cap.name("account").unwrap_unchecked() }
540 .as_str()
541 .into(),
542 ),
543 ))
544 } else {
545 Ok((Some(attr_str.replace("\"", "")), "{}".into(), None))
546 };
547}
548
549#[proc_macro_error]
605#[proc_macro_attribute]
606pub fn ewasm_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
607 let syn::ItemFn {
608 attrs,
609 vis,
610 sig,
611 block,
612 } = syn::parse_macro_input!(item as syn::ItemFn);
613 let syn::Block { stmts, .. } = *block;
614
615 let name = &sig.ident;
616
617 let (hex_str, abi_str, root_str) = match parse_fn_attr(name.to_string(), attr.to_string()) {
618 Ok(o) => o,
619 Err(e) => abort_call_site!(e),
620 };
621
622 let args = &sig
623 .inputs
624 .iter()
625 .map(|fn_arg| match fn_arg {
626 syn::FnArg::Receiver(r) => {
627 abort!(r, "please use ewasm_fn for function not method")
628 }
629 syn::FnArg::Typed(p) => Box::into_inner(p.ty.clone()),
630 })
631 .map(|ty| match ty {
632 syn::Type::Path(tp) => (
633 tp.path
634 .segments
635 .first()
636 .expect("at least one segment")
637 .ident
638 .clone(),
639 false,
640 ),
641 syn::Type::Reference(tr) => match Box::into_inner(tr.elem) {
642 syn::Type::Path(tp) => (
643 tp.path
644 .segments
645 .first()
646 .expect("at least one segment")
647 .ident
648 .clone(),
649 true,
650 ),
651 _ => abort_call_site!("please pass Path type or Reference type to ewasm_fn_sig"),
652 },
653 _ => abort_call_site!("please pass Path type or Reference type to ewasm_fn_sig"),
654 })
655 .map(|(ident, is_ref)| {
656 if is_ref {
657 format!("&{}", ident).to_ascii_lowercase()
658 } else {
659 format!("{}", ident).to_ascii_lowercase()
660 }
661 })
662 .collect::<Vec<_>>()
663 .join(",");
664 let canonical_fn = format!("{}({})", name, args);
665 let (sig_0, sig_1, sig_2, sig_3) = if let Some(hex_str) = hex_str {
666 let fn_sig = hex::decode(hex_str).expect("function signature is not correct");
667 (fn_sig[0], fn_sig[1], fn_sig[2], fn_sig[3])
668 } else {
669 let fn_sig = get_function_signature(&canonical_fn);
670 (fn_sig[0], fn_sig[1], fn_sig[2], fn_sig[3])
671 };
672 let abi_info = Ident::new(
673 &format!("{}_ABI", name.to_string().to_ascii_uppercase()),
674 Span::call_site(),
675 );
676 let sig_name = Ident::new(
677 &format!("{}_SIG", name.to_string().to_ascii_uppercase()),
678 Span::call_site(),
679 );
680 let result = if let Some(root_str) = root_str {
681 let addr = root_str.replace("\"", "");
682 quote! {
683 pub const #sig_name : [u8; 4] = [#sig_0, #sig_1, #sig_2, #sig_3];
684 pub(crate) const #abi_info: &'static str = #abi_str;
685
686 #[cfg(target_arch = "wasm32")]
687 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
688 #(#attrs)*
689 #vis #sig {
690 if sewup::utils::caller() != sewup::types::Address::from_str(#addr)? {
691 return Err(sewup::errors::HandlerError::Unauthorized.into())
692 }
693 #(#stmts)*
694 }
695 }
696 } else {
697 quote! {
698 pub const #sig_name : [u8; 4] = [#sig_0, #sig_1, #sig_2, #sig_3];
699 pub(crate) const #abi_info: &'static str = #abi_str;
700
701 #[cfg(target_arch = "wasm32")]
702 #[cfg(not(any(feature = "constructor", feature = "constructor-test")))]
703 #(#attrs)*
704 #vis #sig {
705 #(#stmts)*
706 }
707 }
708 };
709 result.into()
710}
711
712#[proc_macro_error]
714#[proc_macro_attribute]
715pub fn ewasm_constructor(_attr: TokenStream, item: TokenStream) -> TokenStream {
716 let mut input = syn::parse_macro_input!(item as syn::ItemFn);
717 let default_name = Ident::new("__constructor", Span::call_site());
718 if input.sig.ident != "__constructor" {
719 input.sig.ident = default_name;
720 }
721 let result = quote! {
722 #[cfg(target_arch = "wasm32")]
723 #[cfg(any(feature = "constructor", feature = "constructor-test"))]
724 #[no_mangle]
725 #input
726
727 #[cfg(target_arch = "wasm32")]
728 #[cfg(feature = "constructor-test")]
729 #[no_mangle]
730 pub fn main() {
731 __constructor();
732 }
733 };
734 result.into()
735}
736
737#[proc_macro_error]
794#[proc_macro_attribute]
795pub fn ewasm_lib_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
796 let input = syn::parse_macro_input!(item as syn::ItemFn);
797 let name = &input.sig.ident;
798
799 let (hex_str, abi_str, _root_str) = match parse_fn_attr(name.to_string(), attr.to_string()) {
800 Ok(o) => o,
801 Err(e) => abort_call_site!(e),
802 };
803
804 let inputs = &input.sig.inputs;
805
806 let (sig_0, sig_1, sig_2, sig_3) = if let Some(hex_str) = hex_str {
807 let fn_sig = hex::decode(hex_str).expect("function signature is not correct");
808 (fn_sig[0], fn_sig[1], fn_sig[2], fn_sig[3])
809 } else {
810 let args = &inputs
811 .iter()
812 .map(|fn_arg| match fn_arg {
813 syn::FnArg::Receiver(r) => {
814 abort!(r, "please use ewasm_fn for function not method")
815 }
816 syn::FnArg::Typed(p) => Box::into_inner(p.ty.clone()),
817 })
818 .map(|ty| match ty {
819 syn::Type::Path(tp) => (
820 tp.path
821 .segments
822 .first()
823 .expect("at least one segment")
824 .ident
825 .clone(),
826 false,
827 ),
828 syn::Type::Reference(tr) => match Box::into_inner(tr.elem) {
829 syn::Type::Path(tp) => (
830 tp.path
831 .segments
832 .first()
833 .expect("at least one segment")
834 .ident
835 .clone(),
836 true,
837 ),
838 _ => {
839 abort_call_site!("please pass Path type or Reference type to ewasm_fn_sig")
840 }
841 },
842 _ => abort_call_site!("please pass Path type or Reference type to ewasm_fn_sig"),
843 })
844 .map(|(ident, is_ref)| {
845 if is_ref {
846 format!("&{}", ident).to_ascii_lowercase()
847 } else {
848 format!("{}", ident).to_ascii_lowercase()
849 }
850 })
851 .collect::<Vec<_>>()
852 .join(",");
853 let canonical_fn = format!("{}({})", name, args);
854 let fn_sig = get_function_signature(&canonical_fn);
855 (fn_sig[0], fn_sig[1], fn_sig[2], fn_sig[3])
856 };
857 let sig_name = Ident::new(
858 &format!("{}_SIG", name.to_string().to_ascii_uppercase()),
859 Span::call_site(),
860 );
861 let abi_info = Ident::new(
862 &format!("{}_ABI", name.to_string().to_ascii_uppercase()),
863 Span::call_site(),
864 );
865 let result = quote! {
866 pub const #sig_name: [u8; 4] = [#sig_0, #sig_1, #sig_2, #sig_3];
867 pub const #abi_info: &'static str = #abi_str;
868
869 #[cfg(not(target_arch = "wasm32"))]
870 #[allow(unused)]
871 pub fn #name(#inputs) {}
872
873 #[cfg(target_arch = "wasm32")]
874 #input
875 };
876 result.into()
877}
878
879#[proc_macro_error]
929#[proc_macro]
930pub fn ewasm_fn_sig(item: TokenStream) -> TokenStream {
931 write_function_signature(&item.to_string())
932 .parse()
933 .expect("can not generate function signature")
934}
935
936#[proc_macro]
942pub fn ewasm_input(item: TokenStream) -> TokenStream {
943 let re = unsafe { Regex::new(r"(?P<instance>.*)\s+for\s+(?P<sig>.*)").unwrap_unchecked() };
944 if let Ok(Some(cap)) = re.captures(&item.to_string()) {
945 let sig = unsafe { cap.name("sig").unwrap_unchecked() }.as_str();
946 let instance = unsafe { cap.name("instance").unwrap_unchecked() }.as_str();
947 let output = if instance == "None" {
948 format!("{}.to_vec()", write_function_signature(sig),)
949 } else {
950 format!(
951 "{{
952 let mut input = {}.to_vec();
953 input.append(&mut bincode::serialize(&{}).unwrap());
954 input
955 }}",
956 write_function_signature(sig),
957 instance
958 )
959 };
960 output.parse().expect("ewasm input transform unexpected")
961 } else {
962 panic!("ewasm_input input incorrect")
963 }
964}
965
966#[proc_macro_error]
997#[proc_macro]
998pub fn ewasm_input_from(item: TokenStream) -> TokenStream {
999 let re = unsafe {
1000 Regex::new(r"^(?P<contract>\w+)\s+move\s+(?P<name>[^,]+),?(?P<error_handler>.*)")
1001 .unwrap_unchecked()
1002 };
1003 if let Ok(Some(cap)) = re.captures(&item.to_string()) {
1004 let contract = Ident::new(
1005 unsafe { cap.name("contract").unwrap_unchecked() }.as_str(),
1006 Span::call_site(),
1007 );
1008 let name_result: syn::Result<syn::ExprPath> =
1009 syn::parse_str(unsafe { cap.name("name").unwrap_unchecked() }.as_str());
1010 let name = if let Ok(name) = name_result {
1011 name
1012 } else {
1013 abort_call_site!(
1014 "`{}` is not an ExprPath",
1015 unsafe { cap.name("name").unwrap_unchecked() }.as_str()
1016 );
1017 };
1018 let error_handler = unsafe { cap.name("error_handler").unwrap_unchecked() }.as_str();
1019 return if error_handler.is_empty() {
1020 quote! {
1021 #name(sewup::bincode::deserialize(&#contract.input_data[4..])
1022 .map_err(|e| anyhow::anyhow!("contract input deserialize error: {}", e))?
1023 )
1024 }
1025 } else {
1026 let closure: syn::Result<syn::ExprClosure> = syn::parse_str(error_handler);
1027 if let Ok(closure) = closure {
1028 quote! {
1029 #name(sewup::bincode::deserialize(&#contract.input_data[4..]).map_err(#closure)?)
1030 }
1031 } else {
1032 abort_call_site!("`{}` is not an closure input for map_err", error_handler);
1033 }
1034 }
1035 .into();
1036 } else {
1037 abort_call_site!(
1038 r#"fail to parsing ewasm_input_from,
1039 please use
1040 `ewasm_input_from( contract move handler )
1041 or
1042 `ewasm_input_from( contract move handler, closure_for_map_err)`
1043 "#
1044 );
1045 }
1046}
1047
1048#[proc_macro]
1050pub fn ewasm_output_from(item: TokenStream) -> TokenStream {
1051 format!(
1052 r#"sewup::bincode::serialize(&{}).expect("fail to serialize in `ewasm_output_from`")"#,
1053 item,
1054 )
1055 .parse()
1056 .expect("ewasm output format unexpected")
1057}
1058
1059#[cfg(feature = "kv")]
1070#[proc_macro_derive(Key)]
1071pub fn derive_key(item: TokenStream) -> TokenStream {
1072 let input = syn::parse_macro_input!(item as syn::DeriveInput);
1073 let sturct_name = &input.ident;
1074 return quote! {
1075 #[cfg(target_arch = "wasm32")]
1076 impl sewup::kv::traits::Key for #sturct_name {}
1077 }
1078 .into();
1079}
1080
1081#[cfg(feature = "kv")]
1092#[proc_macro_derive(Value)]
1093pub fn derive_value(item: TokenStream) -> TokenStream {
1094 let input = syn::parse_macro_input!(item as syn::DeriveInput);
1095 let sturct_name = &input.ident;
1096 return quote! {
1097 #[cfg(target_arch = "wasm32")]
1098 impl sewup::kv::traits::Value for #sturct_name {}
1099 }
1100 .into();
1101}
1102
1103#[cfg(feature = "rdb")]
1193#[proc_macro_derive(Table, attributes(belongs_to, belongs_none_or))]
1194pub fn derive_table(item: TokenStream) -> TokenStream {
1195 let input = syn::parse_macro_input!(item as syn::DeriveInput);
1196 let attrs = &input.attrs;
1197 let mut belongs_to: Option<String> = None;
1198 let mut belongs_none_or: Option<String> = None;
1199 for a in attrs.iter() {
1200 let syn::Attribute { path, tokens, .. } = a;
1201 let attr_name = path.segments.first().map(|s| s.ident.to_string());
1202 if Some("belongs_to".to_string()) == attr_name {
1203 belongs_to = Some(
1204 tokens
1205 .to_string()
1206 .strip_prefix('(')
1207 .expect("#[belongs_to(table_name)] is not correct")
1208 .strip_suffix(')')
1209 .expect("#[belongs_to(table_name)] is not correct")
1210 .to_string(),
1211 );
1212 }
1213 if Some("belongs_none_or".to_string()) == attr_name {
1214 belongs_none_or = Some(
1215 tokens
1216 .to_string()
1217 .strip_prefix('(')
1218 .expect("#[belongs_none_or(table_name)] is not correct")
1219 .strip_suffix(')')
1220 .expect("#[belongs_none_or(table_name)] is not correct")
1221 .to_string(),
1222 );
1223 }
1224 }
1225 let struct_name = &input.ident;
1226 let fields_with_type = match &input.data {
1227 syn::Data::Struct(syn::DataStruct {
1228 fields: syn::Fields::Named(f),
1229 ..
1230 }) => f
1231 .clone()
1232 .named
1233 .into_pairs()
1234 .map(|p| p.into_value())
1235 .map(|f| (f.ident.unwrap(), f.ty))
1236 .collect::<Vec<_>>(),
1237 _ => abort!(&input.ident, "Table derive only use for struct"),
1238 };
1239
1240 let mut wrapper_fields = vec![(
1241 Ident::new("id", Span::call_site()),
1242 syn::Type::Path(syn::TypePath {
1243 qself: None,
1244 path: syn::parse("Option<usize>".parse().unwrap()).unwrap(),
1245 }),
1246 )];
1247 wrapper_fields.append(
1248 &mut fields_with_type
1249 .iter()
1250 .map(|(f, t)| {
1251 (
1252 f.clone(),
1253 syn::parse(quote!(Option<#t>).to_string().parse().unwrap()).unwrap(),
1254 )
1255 })
1256 .collect::<Vec<_>>(),
1257 );
1258 let wrapper_field_names = wrapper_fields.iter().map(|(f, _)| f);
1259 let wrapper_field_types = wrapper_fields.iter().map(|(_, t)| t);
1260 let field_names = fields_with_type.iter().map(|(f, _)| f);
1261 let clone_field_names = field_names.clone();
1262 let clone_field_names2 = field_names.clone();
1263 let clone_field_names3 = field_names.clone();
1264 let clone_field_names4 = field_names.clone();
1265 let clone_field_names5 = field_names.clone();
1266 let field_types = fields_with_type.iter().map(|(_, t)| t);
1267
1268 let protocol_name = Ident::new(&format!("{}Protocol", struct_name), Span::call_site());
1269 let wrapper_name = Ident::new(&format!("{}Wrapper", struct_name), Span::call_site());
1270 let captal_name = Ident::new(
1271 &format!("{}", struct_name).to_ascii_uppercase(),
1272 Span::call_site(),
1273 );
1274 let lower_name = Ident::new(
1275 &format!("{}", struct_name).to_ascii_lowercase(),
1276 Span::call_site(),
1277 );
1278 let mut output = quote!(
1279 impl sewup::rdb::traits::Record for #struct_name {}
1280
1281 #[cfg_attr(any(feature = "debug", test), derive(Debug))]
1282 #[derive(Clone, sewup::Serialize, sewup::Deserialize)]
1283 pub struct #protocol_name {
1284 pub select_fields: Option<std::collections::HashSet::<String>>,
1285 pub filter: bool,
1286 pub records: Vec<#wrapper_name>
1287 }
1288
1289 impl #protocol_name {
1290 pub fn set_select_fields(&mut self, fields: Vec<String>) {
1291 if fields.is_empty() {
1292 self.select_fields = None;
1293 } else {
1294 let mut select_fields = std::collections::HashSet::<String>::new();
1295 for field in fields.iter() {
1296 select_fields.insert(field.into());
1297 }
1298 self.select_fields = Some(select_fields);
1299 }
1300 }
1301 }
1302
1303 impl Default for #protocol_name {
1304 fn default() -> Self {
1305 Self {
1306 select_fields: None,
1307 filter: false,
1308 records: vec![Default::default()]
1309 }
1310 }
1311 }
1312
1313 impl From<#struct_name> for #protocol_name {
1314 fn from(instance: #struct_name) -> Self {
1315 Self {
1316 select_fields: None,
1317 filter: false,
1318 records: vec![instance.into()]
1319 }
1320 }
1321 }
1322
1323 impl From<(usize, #struct_name)> for #protocol_name {
1324 fn from(instance: (usize, #struct_name)) -> Self {
1325 Self {
1326 select_fields: None,
1327 filter: false,
1328 records: vec![instance.into()]
1329 }
1330 }
1331 }
1332
1333 impl From<Vec<#struct_name>> for #protocol_name {
1334 fn from(instances: Vec<#struct_name>) -> Self {
1335 Self {
1336 select_fields: None,
1337 filter: false,
1338 records: instances.into_iter().map(|i| i.into()).collect::<Vec<_>>()
1339 }
1340 }
1341 }
1342
1343 impl From<Vec<(usize, #struct_name)>> for #protocol_name {
1344 fn from(instances: Vec<(usize, #struct_name)>) -> Self {
1345 Self {
1346 select_fields: None,
1347 filter: false,
1348 records: instances.into_iter().map(|i| i.into()).collect::<Vec<_>>()
1349 }
1350 }
1351 }
1352
1353 impl From<Vec<#wrapper_name>> for #protocol_name {
1354 fn from(records: Vec<#wrapper_name>) -> Self {
1355 Self {
1356 select_fields: None,
1357 filter: false,
1358 records,
1359 }
1360 }
1361 }
1362
1363 pub mod #captal_name {
1364 use sewup_derive::ewasm_fn_sig;
1365 pub const GET_SIG: [u8; 4] = ewasm_fn_sig!(#struct_name::get());
1366 pub const CREATE_SIG: [u8; 4] = ewasm_fn_sig!(#struct_name::create());
1367 pub const UPDATE_SIG: [u8; 4] = ewasm_fn_sig!(#struct_name::update());
1368 pub const DELETE_SIG: [u8; 4] = ewasm_fn_sig!(#struct_name::delete());
1369 }
1370
1371 #[cfg_attr(any(feature = "debug", test), derive(Debug))]
1372 #[derive(Default, Clone, sewup::Serialize, sewup::Deserialize)]
1373 pub struct #wrapper_name {
1374 #(pub #wrapper_field_names: #wrapper_field_types,)*
1375 }
1376
1377 impl From<#struct_name> for #wrapper_name {
1378 fn from(instance: #struct_name) -> Self {
1379 Self {
1380 id: None,
1381 #(#field_names: Some(instance.#field_names),)*
1382 }
1383 }
1384 }
1385
1386 impl From<(usize, #struct_name)> for #wrapper_name {
1387 fn from(t: (usize, #struct_name)) -> Self {
1388 Self {
1389 id: Some(t.0),
1390 #(#clone_field_names5: Some(t.1.#clone_field_names5),)*
1391 }
1392 }
1393 }
1394
1395 impl From<#wrapper_name> for #struct_name {
1396 fn from(wrapper: #wrapper_name) -> Self {
1397 Self {
1398 #(#clone_field_names: wrapper.#clone_field_names.expect("#clone_field_names field missing"),)*
1399 }
1400 }
1401 }
1402 #[cfg(target_arch = "wasm32")]
1403 pub mod #lower_name {
1404 use super::*;
1405 pub type Protocol = #protocol_name;
1406 pub type Wrapper = #wrapper_name;
1407 pub type _InstanceType = #struct_name;
1408 pub fn get(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
1409 let table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
1410 if proc.filter {
1411 let mut raw_output: Vec<Wrapper> = Vec::new();
1412 for (idx, r) in table.all_records()?.drain(..).enumerate(){
1413 let mut all_field_match = true;
1414 #(
1415 paste::paste! {
1416 let [<#clone_field_names2 _filed_filter>] : Option<#field_types> =
1417 sewup::utils::get_field_by_name(proc.records[0].clone(), stringify!(#clone_field_names2));
1418
1419 let [<#clone_field_names2 _field>] : #field_types =
1420 sewup::utils::get_field_by_name(&r, stringify!(#clone_field_names2));
1421
1422 if [<#clone_field_names2 _filed_filter>].is_some() {
1423 all_field_match &=
1424 [<#clone_field_names2 _filed_filter>].unwrap()
1425 == [<#clone_field_names2 _field>];
1426 }
1427 }
1428 )*
1429
1430 if all_field_match {
1431 raw_output.push((idx + 1, r).into());
1432 }
1433 }
1434 if let Some(select_fields) = proc.select_fields {
1435 for w in raw_output.iter_mut() {
1436 #(
1437 if (! select_fields.contains(stringify!(#clone_field_names3)))
1438 && stringify!(#clone_field_names3) != "id" {
1439 w.#clone_field_names3 = None;
1440 }
1441 )*
1442 }
1443 }
1444 let p: #protocol_name = raw_output.into();
1445 Ok(p.into())
1446 } else {
1447 let raw_output = table.get_record(proc.records[0].id.unwrap_or_default())?;
1448 let mut output_proc: Protocol = raw_output.into();
1449 output_proc.records[0].id = proc.records[0].id;
1450 Ok(output_proc.into())
1451 }
1452 }
1453 pub fn create(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
1454 let mut table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
1455 let mut output_proc = proc.clone();
1456 output_proc.records[0].id = Some(table.add_record(proc.records[0].clone().into())?);
1457 table.commit()?;
1458 Ok(output_proc.into())
1459 }
1460 pub fn update(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
1461 let mut table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
1462 let id = proc.records[0].id.unwrap_or_default();
1463 table.update_record(id, Some(proc.records[0].clone().into()))?;
1464 table.commit()?;
1465 Ok(proc.into())
1466 }
1467 pub fn delete(proc: Protocol) -> sewup::Result<sewup::primitives::EwasmAny> {
1468 let mut table = sewup::rdb::Db::load(None)?.table::<_InstanceType>()?;
1469 let id = proc.records[0].id.unwrap_or_default();
1470 table.update_record(id, None)?;
1471 table.commit()?;
1472 Ok(proc.into())
1473 }
1474 }
1475 #[cfg(not(target_arch = "wasm32"))]
1476 pub mod #lower_name {
1477 use super::*;
1478 pub type Protocol = #protocol_name;
1479 pub type Wrapper = #wrapper_name;
1480 pub type _InstanceType = #struct_name;
1481 pub type Query = Wrapper;
1482
1483 #[inline]
1484 pub fn protocol(instance: _InstanceType) -> Protocol {
1485 instance.into()
1486 }
1487 impl Protocol {
1488 pub fn set_id(&mut self, id: usize) {
1489 self.records[0].id = Some(id);
1490 }
1491 pub fn is_empty(&self) -> bool {
1492 #(self.records[0].#clone_field_names4.is_none() && )*
1493 true
1494 }
1495 }
1496 pub fn query(instance: _InstanceType) -> Wrapper {
1497 instance.into()
1498 }
1499
1500 impl From<Query> for Protocol {
1501 fn from(instance: Query) -> Self {
1502 Self {
1503 select_fields: None,
1504 filter: true,
1505 records: vec![instance.into()]
1506 }
1507 }
1508 }
1509 }
1510 ).to_string();
1511
1512 if let Some(parent_table) = belongs_to {
1513 let lower_parent_table = &format!("{}", &parent_table).to_ascii_lowercase();
1514 let parent_table = Ident::new(&parent_table, Span::call_site());
1515 let lower_parent_table_ident = Ident::new(&lower_parent_table, Span::call_site());
1516 let field_name = &format!("{}_id", lower_parent_table);
1517
1518 output += "e! {
1519 impl #struct_name {
1520 pub fn #lower_parent_table_ident (&self) -> sewup::Result<#parent_table> {
1521 let id: usize = sewup::utils::get_field_by_name(self, #field_name);
1522 let parent_table = sewup::rdb::Db::load(None)?.table::<#parent_table>()?;
1523 parent_table.get_record(id)
1524 }
1525 }
1526 }
1527 .to_string();
1528 }
1529
1530 if let Some(parent_table) = belongs_none_or {
1531 let lower_parent_table = &format!("{}", &parent_table).to_ascii_lowercase();
1532 let parent_table = Ident::new(&parent_table, Span::call_site());
1533 let lower_parent_table_ident = Ident::new(&lower_parent_table, Span::call_site());
1534 let field_name = &format!("{}_id", lower_parent_table);
1535
1536 output += "e! {
1537 impl #struct_name {
1538 pub fn #lower_parent_table_ident (&self) -> sewup::Result<Option<#parent_table>> {
1539 let id: Option<usize> = sewup::utils::get_field_by_name(self, #field_name);
1540 let parent_table = sewup::rdb::Db::load(None)?.table::<#parent_table>()?;
1541 if let Some(id) = id {
1542 match parent_table.get_record(id) {
1543 Ok(r) => Ok(Some(r)),
1544 Err(e) => Err(e)
1545 }
1546 } else {
1547 Ok(None)
1548 }
1549 }
1550 }
1551 }
1552 .to_string();
1553 }
1554
1555 output.parse().unwrap()
1556}
1557
1558#[proc_macro_error]
1586#[proc_macro_attribute]
1587pub fn ewasm_test(attr: TokenStream, item: TokenStream) -> TokenStream {
1588 let mod_re = unsafe {
1589 Regex::new(r"^mod (?P<mod_name>[^\{\s]*)(?P<to_first_bracket>[^\{]*\{)").unwrap_unchecked()
1590 };
1591 let fn_re = unsafe {
1592 Regex::new(r"^fn (?P<fn_name>[^\(\s]*)(?P<to_first_bracket>[^\{]*\{)").unwrap_unchecked()
1593 };
1594 let context = item.to_string();
1595 let mod_captures = mod_re.captures(&context).unwrap();
1596 let fn_captures = fn_re.captures(&context).unwrap();
1597 if mod_captures.is_some() {
1598 let attr_str = attr.to_string().replace(" ", "");
1599 let runtime_log_option = if attr_str.is_empty() {
1600 "".to_string()
1601 } else {
1602 let options = attr_str.split('=').collect::<Vec<_>>();
1603 match options[0].to_lowercase().as_str() {
1604 "log" => format!(".set_log_file({:?}.into())", options[1]),
1605 _ => abort_call_site!("no support option"),
1606 }
1607 };
1608 let template = r#"
1609 #[cfg(test)]
1610 mod $mod_name {
1611 use sewup::bincode;
1612 use sewup::runtimes::{handler::ContractHandler, test::TestRuntime};
1613 use std::cell::RefCell;
1614 use std::path::Path;
1615 use std::path::PathBuf;
1616 use std::process::Command;
1617 use std::sync::Arc;
1618 use sewup::runtimes::traits::RT;
1619
1620 fn _build_wasm(opt: Option<String>) -> String {
1621 let cargo_cmd = format!("cargo build --release --target=wasm32-unknown-unknown {}", opt.unwrap_or_default());
1622 let output = Command::new("sh")
1623 .arg("-c")
1624 .arg(&cargo_cmd)
1625 .output()
1626 .expect("failed to build wasm binary");
1627 if !output.status.success() {
1628 panic!("return code not success: fail to build wasm binary")
1629 }
1630 let pkg_name = env!("CARGO_PKG_NAME");
1631 let base_dir = env!("CARGO_MANIFEST_DIR");
1632 let wasm_binary = format!(
1633 "{}/target/wasm32-unknown-unknown/release/{}.wasm",
1634 base_dir,
1635 pkg_name.replace("-", "_")
1636 );
1637
1638 if !Path::new(&wasm_binary).exists() {
1639 panic!("wasm binary missing")
1640 }
1641 wasm_binary
1642 }
1643
1644 fn _build_runtime_and_runner() -> (
1645 Arc<RefCell<TestRuntime>>,
1646 impl Fn(Arc<RefCell<TestRuntime>>, Option<&str>, &str, [u8; 4], Option<&[u8]>, Vec<u8>) -> (),
1647 ) {
1648 let rt = Arc::new(RefCell::new(TestRuntime::default()"#.to_string()
1649 + &runtime_log_option
1650 + r#"));
1651 let mut h = ContractHandler {
1652 call_data: None,
1653 rt: Some(rt.clone())
1654 };
1655
1656 match h.run_fn(_build_wasm(Some("--features=constructor-test".to_string())), None, 1_000_000_000_000) {
1657 Ok(_) => (),
1658 Err(e) => {
1659 panic!("vm run constructor error: {:?}", e);
1660 }
1661 };
1662
1663 (rt,
1664 |runtime: Arc<RefCell<TestRuntime>>,
1665 caller: Option<&str>,
1666 fn_name: &str,
1667 sig: [u8; 4],
1668 input_data: Option<&[u8]>,
1669 expect_output: Vec<u8>| {
1670 let mut h = ContractHandler {
1671 call_data: Some(_build_wasm(None)),
1672 rt: Some(runtime.clone())
1673 };
1674
1675 match h.execute(caller.clone(), sig, input_data, 1_000_000_000_000) {
1676 Ok(r) => {
1677 if !(*r.output_data == *expect_output) {
1678 if let Some(caller) = caller {
1679 eprintln!("vm caller : {}", caller);
1680 }
1681
1682 if let Ok(output_msg) = std::str::from_utf8(&r.output_data) {
1683 eprintln!("vm msg : \"{}\"", output_msg);
1684 }
1685 eprintln!("vm output : {:?}", r.output_data);
1686
1687 if let Ok(expect_msg) = std::str::from_utf8(&expect_output) {
1688 eprintln!("expected : \"{}\"", expect_msg);
1689 }
1690 eprintln!("expected : {:?}", expect_output);
1691 panic!("function `{}` output is unexpected", fn_name);
1692 }
1693 },
1694 Err(e) => {
1695 panic!("vm error: {:?}", e);
1696 }
1697 }
1698 },
1699 )
1700 }
1701
1702 #[test]
1703 fn _compile_runtime_test() {
1704 _build_wasm(None);
1705 }
1706
1707 #[test]
1708 fn _compile_constructor_test() {
1709 _build_wasm(Some("--features=constructor-test".to_string()));
1710 }"#;
1711 return mod_re
1712 .replace(&context, &template)
1713 .to_string()
1714 .parse()
1715 .unwrap();
1716 } else if fn_captures.is_some() {
1717 let attr_str = attr.to_string().replace(" ", "");
1718 if !attr_str.is_empty() {
1719 abort_call_site!("no support option when wrapping on function")
1720 };
1721 return fn_re
1722 .replace(
1723 &context,
1724 r#"
1725 #[test]
1726 fn $fn_name () {
1727 let (_runtime, _run_wasm_fn) = _build_runtime_and_runner();
1728 let mut _bin: Vec<u8> = Vec::new();"#,
1729 )
1730 .to_string()
1731 .parse()
1732 .unwrap();
1733 } else {
1734 abort_call_site!("parse mod or function for testing error")
1735 }
1736}
1737#[proc_macro_error]
1756#[proc_macro]
1757pub fn ewasm_assert_eq(item: TokenStream) -> TokenStream {
1758 let re = unsafe {
1759 Regex::new(r#"^(?P<fn_name>[^(]+?)\((?P<params>[^)]*?)\)\s*(by)?\s*(?P<caller>"[^"]*")?\s*,(?P<equivalence>.*)"#).unwrap_unchecked()
1760 };
1761 if let Ok(Some(cap)) = re.captures(&item.to_string().replace("\n", "")) {
1762 let fn_name = unsafe { cap.name("fn_name").unwrap_unchecked() }
1763 .as_str()
1764 .replace(" ", "");
1765 let params = unsafe { cap.name("params").unwrap_unchecked() }
1766 .as_str()
1767 .replace(" ", "");
1768 let equivalence = unsafe { cap.name("equivalence").unwrap_unchecked() }.as_str();
1769 let caller = cap
1770 .name("caller")
1771 .map(|c| format!("Some({})", c.as_str()))
1772 .unwrap_or_else(|| "None".to_string());
1773 if params.is_empty() {
1774 format!(
1775 r#"_run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), None, {});"#,
1776 caller, fn_name, fn_name, equivalence
1777 )
1778 .parse()
1779 .unwrap()
1780 } else {
1781 format!(
1782 r#"_bin = bincode::serialize(&{}).unwrap();
1783 _run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), Some(&_bin), {});"#,
1784 params, caller, fn_name, fn_name, equivalence
1785 )
1786 .parse()
1787 .unwrap()
1788 }
1789 } else {
1790 abort_call_site!("fail to parsing function in ewasm_assert_eq");
1791 }
1792}
1793
1794#[proc_macro_error]
1800#[proc_macro]
1801pub fn ewasm_auto_assert_eq(item: TokenStream) -> TokenStream {
1802 let re = unsafe {
1803 Regex::new(r#"^(?P<fn_name>[^(]+?)\((?P<params>[^)]*?)\)\s*(by)?\s*(?P<caller>"[^"]*")?\s*,(?P<equivalence>.*)"#).unwrap_unchecked()
1804 };
1805 if let Ok(Some(cap)) = re.captures(&item.to_string().replace("\n", "")) {
1806 let fn_name = unsafe { cap.name("fn_name").unwrap_unchecked() }.as_str();
1807 let params = unsafe { cap.name("params").unwrap_unchecked() }
1808 .as_str()
1809 .replace(" ", "");
1810 let equivalence = unsafe { cap.name("equivalence").unwrap_unchecked() }.as_str();
1811 let caller = cap
1812 .name("caller")
1813 .map(|c| format!("Some({})", c.as_str()))
1814 .unwrap_or_else(|| "None".to_string());
1815 if params.is_empty() {
1816 format!(
1817 r#"_run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), None, sewup_derive::ewasm_output_from!({}));"#,
1818 caller, fn_name, fn_name, equivalence
1819 )
1820 .parse()
1821 .unwrap()
1822 } else {
1823 format!(
1824 r#"_bin = bincode::serialize(&{}).unwrap();
1825 _run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), Some(&_bin), sewup_derive::ewasm_output_from!({}));"#,
1826 params, caller, fn_name, fn_name, equivalence
1827 )
1828 .parse()
1829 .unwrap()
1830 }
1831 } else {
1832 abort_call_site!("fail to parsing function in fn_select");
1833 }
1834}
1835
1836#[proc_macro_error]
1855#[proc_macro]
1856pub fn ewasm_assert_ok(item: TokenStream) -> TokenStream {
1857 let re = unsafe {
1858 Regex::new(
1859 r#"^(?P<fn_name>[^(]+?)\((?P<params>[^)]*?)\)\s*(by)?\s*(?P<caller>"[^"]*")?\s*"#,
1860 )
1861 .unwrap_unchecked()
1862 };
1863 if let Ok(Some(cap)) = re.captures(&item.to_string().replace("\n", "")) {
1864 let fn_name = unsafe { cap.name("fn_name").unwrap_unchecked() }.as_str();
1865 let params = unsafe { cap.name("params").unwrap_unchecked() }
1866 .as_str()
1867 .replace(" ", "");
1868 let caller = cap
1869 .name("caller")
1870 .map(|c| format!("Some({})", c.as_str()))
1871 .unwrap_or_else(|| "None".to_string());
1872 if params.is_empty() {
1873 format!(
1874 r#"_run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), None, Vec::with_capacity(0));"#,
1875 caller, fn_name, fn_name
1876 )
1877 .parse()
1878 .unwrap()
1879 } else {
1880 format!(
1881 r#"_bin = bincode::serialize(&{}).unwrap();
1882 _run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), Some(&_bin), Vec::with_capacity(0));"#,
1883 params, caller, fn_name, fn_name
1884 )
1885 .parse()
1886 .unwrap()
1887 }
1888 } else {
1889 abort_call_site!("fail to parsing function in fn_select");
1890 }
1891}
1892
1893#[proc_macro_error]
1899#[proc_macro]
1900pub fn ewasm_rusty_assert_ok(item: TokenStream) -> TokenStream {
1901 let re = unsafe {
1902 Regex::new(
1903 r#"^(?P<fn_name>[^(]+?)\((?P<params>[^)]*?)\)\s*(by)?\s*(?P<caller>"[^"]*")?\s*"#,
1904 )
1905 .unwrap_unchecked()
1906 };
1907 if let Ok(Some(cap)) = re.captures(&item.to_string().replace("\n", "")) {
1908 let fn_name = unsafe { cap.name("fn_name").unwrap_unchecked() }.as_str();
1909 let params = unsafe { cap.name("params").unwrap_unchecked() }
1910 .as_str()
1911 .replace(" ", "");
1912 let caller = cap
1913 .name("caller")
1914 .map(|c| format!("Some({})", c.as_str()))
1915 .unwrap_or_else(|| "None".to_string());
1916 if params.is_empty() {
1917 format!(
1918 r#"_run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), None, vec![0, 0, 0, 0]);"#,
1919 caller, fn_name, fn_name
1920 )
1921 .parse()
1922 .unwrap()
1923 } else {
1924 format!(
1925 r#"_bin = bincode::serialize(&{}).unwrap();
1926 _run_wasm_fn( _runtime.clone(), {}, "{}", ewasm_fn_sig!({}), Some(&_bin), vec![0, 0, 0, 0]);"#,
1927 params, caller, fn_name, fn_name
1928 )
1929 .parse()
1930 .unwrap()
1931 }
1932 } else {
1933 abort_call_site!("fail to parsing function in fn_select");
1934 }
1935}
1936
1937#[proc_macro_error]
1946#[proc_macro]
1947pub fn ewasm_rusty_err_output(item: TokenStream) -> TokenStream {
1948 format!(
1949 r#"bincode::serialize(&({})).expect("can not serialize the output expected from ewasm").to_vec()"#,
1950 &item.to_string()
1951 )
1952 .parse()
1953 .unwrap()
1954}
1955
1956#[proc_macro_error]
1977#[proc_macro]
1978pub fn ewasm_err_output(item: TokenStream) -> TokenStream {
1979 format!("{}.to_string().as_bytes().to_vec()", &item.to_string())
1980 .parse()
1981 .unwrap()
1982}
1983
1984#[allow(non_snake_case)]
1992#[proc_macro_error]
1993#[proc_macro]
1994pub fn SizedString(item: TokenStream) -> TokenStream {
1995 let num = item.to_string();
1996
1997 if let Ok(num) = num.trim().parse::<usize>() {
1998 if num > 0 {
1999 let raw_size = num / 32usize + 1;
2000 return format!("[sewup::types::Raw; {}]", raw_size)
2001 .parse()
2002 .unwrap();
2003 }
2004 }
2005 panic!("The input of SizedString! should be a greator than zero integer")
2006}
2007
2008#[proc_macro]
2013pub fn ewasm_call_only_by(item: TokenStream) -> TokenStream {
2014 let input = item.to_string().replace(" ", "");
2015 let output = if input.starts_with('"') {
2016 let addr = input.replace("\"", "");
2017 quote! {
2018 if sewup::utils::caller() != sewup::types::Address::from_str(#addr)? {
2019 return Err(sewup::errors::HandlerError::Unauthorized.into())
2020 }
2021 }
2022 } else {
2023 let addr = Ident::new(&input, Span::call_site());
2024 quote! {
2025 if sewup::utils::caller() != sewup::types::Address::from_str(#addr)? {
2026 return Err(sewup::errors::HandlerError::Unauthorized.into())
2027 }
2028 }
2029 };
2030
2031 output.into()
2032}
2033
2034#[proc_macro]
2042pub fn ewasm_storage_debug(item: TokenStream) -> TokenStream {
2043 let desc = item.to_string();
2044 quote!({
2045 let __cloned = _runtime.clone();
2046 let __borrowed = __cloned.borrow();
2047 let __storage = __borrowed.get_storage(&[0; 20]);
2048 eprintln!("{}", sewup::utils::pretty_print_storage(#desc, __storage));
2049 })
2050 .into()
2051}