zink_codegen/
revert.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Revert macro

use proc_macro::TokenStream;
use proc_macro2::{Literal, Span};
use quote::{quote, ToTokens};
use syn::{
    parse::{Parse, ParseStream},
    parse2, Expr, Ident, LitStr, Token,
};

/// Revert with message
pub fn parse(input: LitStr) -> TokenStream {
    let message = input.value();
    let len = message.len() as i32;
    if len > 128 {
        panic!("Only support revert message less than 128 bytes atm.");
    }

    // TODO: handle the string correctly
    let lit = Literal::string(&message.replace("\"", ""));
    let rev = Ident::new(
        &format!(
            "revert{}",
            match len {
                len if len > 96 => 4,
                len if len > 64 => 3,
                len if len > 32 => 2,
                len if len > 0 => 1,
                _ => panic!("Only support revert message less than 128 bytes atm."),
            },
        ),
        Span::call_site(),
    );

    quote! {
        unsafe { zink::ffi::asm::#rev(#lit) }
    }
    .into()
}

/// Parse assert macro
pub fn parse_assert(input: AssertInput) -> TokenStream {
    let cond = input.cond;
    let revert: Expr = syn::parse2(
        parse(
            input
                .message
                .unwrap_or(LitStr::new("unknown error", Span::call_site())),
        )
        .into(),
    )
    .expect("Invalid revert message");

    quote! {
        if !#cond {
            #revert
        }
    }
    .into()
}

/// Assert input
pub struct AssertInput {
    pub cond: Expr,
    pub comma: Token![,],
    pub message: Option<LitStr>,
}

impl Parse for AssertInput {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        Ok(AssertInput {
            cond: input.parse()?,
            comma: input.parse()?,
            message: input.parse()?,
        })
    }
}