cainome_parser/tokens/
tuple.rs

1//! A tuple is a collection of types which can be of different types.
2//!
3//! An empty tuple is considered as a unit type `()`, and has its own management
4//! in the [`crate::tokens::CoreBasic`] module.
5//!
6//! A tuple can contain generic in cairo code, however in the ABI,
7//! generic types are actually always replaced by their concrete types.
8//! So a [`Tuple`] is not a generic type itself in the context of cainome.
9use syn::Type;
10
11use super::Token;
12use crate::{CainomeResult, Error};
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct Tuple {
16    pub type_path: String,
17    pub inners: Vec<Token>,
18}
19
20impl Tuple {
21    /// Parses a tuple from a type path.
22    ///
23    /// # Arguments
24    ///
25    /// * `type_path` - The type path to parse.
26    ///
27    /// # Returns
28    ///
29    /// Returns a [`Tuple`] token if the type path is a tuple.
30    /// Returns an error otherwise.
31    ///
32    /// # Example
33    ///
34    /// ```rust
35    /// use cainome_parser::tokens::{Token, Tuple, CoreBasic};
36    ///
37    /// let tuple = Tuple::parse("(core::felt252, core::integer::u64)").unwrap();
38    /// assert_eq!(tuple.type_path, "(core::felt252, core::integer::u64)");
39    /// assert_eq!(tuple.inners.len(), 2);
40    /// assert_eq!(tuple.inners[0], Token::CoreBasic(CoreBasic { type_path: "core::felt252".to_string() }));
41    /// assert_eq!(tuple.inners[1], Token::CoreBasic(CoreBasic { type_path: "core::integer::u64".to_string() }));
42    /// ```
43    pub fn parse(type_path: &str) -> CainomeResult<Self> {
44        let t: Type = syn::parse_str(type_path)?;
45
46        let mut inners = vec![];
47
48        match t {
49            Type::Tuple(t) => {
50                if t.elems.is_empty() {
51                    return Err(Error::TokenInitFailed(
52                        "Unit type `()` is considered as a CoreBasic, not a tuple.".to_string(),
53                    ));
54                }
55
56                for e in t.elems {
57                    let ty = quote::quote!(#e).to_string().replace(' ', "");
58                    inners.push(Token::parse(&ty)?);
59                }
60            }
61            Type::Paren(t) => {
62                // Tuple with one element are under `Paren` variant.
63                let e = t.elem;
64                let ty = quote::quote!(#e).to_string().replace(' ', "");
65                inners.push(Token::parse(&ty)?);
66            }
67            _ => {
68                return Err(Error::TokenInitFailed(format!(
69                    "Tuple couldn't be initialized from `{}`.",
70                    type_path,
71                )));
72            }
73        }
74
75        Ok(Self {
76            type_path: type_path.to_string(),
77            inners,
78        })
79    }
80
81    pub fn apply_alias(&mut self, type_path: &str, alias: &str) {
82        for i in &mut self.inners {
83            i.apply_alias(type_path, alias);
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::tokens::*;
92
93    #[test]
94    fn test_parse_unit_invalid() {
95        assert!(Tuple::parse("()").is_err());
96    }
97
98    #[test]
99    fn test_parse_one_inner() {
100        assert_eq!(
101            Tuple::parse("(core::felt252)").unwrap(),
102            Tuple {
103                type_path: "(core::felt252)".to_string(),
104                inners: vec![Token::CoreBasic(CoreBasic {
105                    type_path: "core::felt252".to_string()
106                }),],
107            }
108        );
109    }
110
111    #[test]
112    fn test_parse_multiple_inners() {
113        assert_eq!(
114            Tuple::parse("(core::felt252, core::integer::u64)").unwrap(),
115            Tuple {
116                type_path: "(core::felt252, core::integer::u64)".to_string(),
117                inners: vec![
118                    Token::CoreBasic(CoreBasic {
119                        type_path: "core::felt252".to_string()
120                    }),
121                    Token::CoreBasic(CoreBasic {
122                        type_path: "core::integer::u64".to_string()
123                    }),
124                ],
125            }
126        );
127    }
128
129    #[test]
130    fn test_parse_other_type_invalid() {
131        assert!(Tuple::parse("module::module2::MyStuct").is_err());
132        assert!(Tuple::parse("core::integer::u64").is_err());
133    }
134}