dharitri_sc_derive/parse/attributes/
payable_attr.rs

1use crate::parse::attributes::util::{clean_string, is_first_char_numeric};
2
3use super::attr_names::*;
4
5pub struct PayableAttribute {
6    pub identifier: String,
7}
8
9impl PayableAttribute {
10    pub fn parse(attr: &syn::Attribute) -> Option<PayableAttribute> {
11        if let Some(first_seg) = attr.path().segments.first() {
12            if first_seg.ident == ATTR_PAYABLE {
13                Some(PayableAttribute {
14                    identifier: extract_token_identifier(attr),
15                })
16            } else {
17                None
18            }
19        } else {
20            None
21        }
22    }
23}
24
25/// Current implementation only works with 1 token name.
26/// Might be extended in the future.
27fn extract_token_identifier(attr: &syn::Attribute) -> String {
28    match attr.meta.clone() {
29        syn::Meta::Path(_) => {
30            // #[payable]
31            "*".to_owned()
32        },
33        syn::Meta::List(list) => {
34            let mut iter = list.tokens.into_iter();
35            let ticker = match iter.next() {
36                Some(proc_macro2::TokenTree::Literal(literal)) => {
37                    let clean = clean_string(literal.to_string());
38                    assert!(
39                        !clean.is_empty(),
40                        "ticker can not be empty. attribute needs 1 string argument: Replace with #[payable(\"*\")] or #[payable(\"REWA\")"
41                    );
42
43                    assert!(!is_first_char_numeric(&clean), "argument can not be a number");
44
45                    if clean
46                    .chars()
47                    .next()
48                    .is_some_and(|s|
49                        s == '*'
50                    ) {
51                        assert!(clean.len() == 1usize, "attribute needs 1 string argument: \"*\", \"REWA\" or token identifier");
52                    }
53
54                    clean
55                },
56                Some(_) => panic!("expected a string as argument"),
57                None => panic!("argument can not be empty. attribute needs 1 string argument: Replace with #[payable(\"*\")] or #[payable(\"REWA\")"),
58            };
59
60            assert!(
61                iter.next().is_none(),
62                "too many tokens in attribute argument"
63            );
64            ticker
65        },
66        syn::Meta::NameValue(_) => panic!(
67            "attribute can not be name value. attribute needs 1 string argument: \"*\" or \"REWA\""
68        ),
69    }
70}