cainome_parser/tokens/
array.rs

1//! This module provides a parser for array types.
2//!
3//! Technically, a `Span` is different than an `Array` in cairo.
4//! However, from a binding point of view, they are both collections,
5//! and we can safely consider them as the same type.
6use super::constants::CAIRO_CORE_SPAN_ARRAY;
7use super::genericity;
8
9use crate::tokens::Token;
10use crate::{CainomeResult, Error};
11
12pub const CAIRO_0_ARRAY: &str = "*";
13
14#[derive(Debug, Clone, PartialEq)]
15pub struct Array {
16    pub type_path: String,
17    pub inner: Box<Token>,
18    pub is_legacy: bool,
19}
20
21impl Array {
22    pub fn parse(type_path: &str) -> CainomeResult<Self> {
23        for a in CAIRO_CORE_SPAN_ARRAY {
24            if type_path.starts_with(a) {
25                let generic_args = genericity::extract_generics_args(type_path)?;
26
27                if generic_args.len() != 1 {
28                    return Err(Error::TokenInitFailed(format!(
29                        "Array/Span are expected exactly one generic argument, found {} in `{}`.",
30                        generic_args.len(),
31                        type_path,
32                    )));
33                }
34
35                let (_, generic_arg_token) = &generic_args[0];
36
37                return Ok(Self {
38                    type_path: type_path.to_string(),
39                    inner: Box::new(generic_arg_token.clone()),
40                    is_legacy: false,
41                });
42            }
43        }
44
45        if let Some(inner_type) = type_path.strip_suffix(CAIRO_0_ARRAY) {
46            return Ok(Self {
47                type_path: type_path.to_string(),
48                inner: Box::new(Token::parse(inner_type)?),
49                is_legacy: true,
50            });
51        }
52
53        Err(Error::TokenInitFailed(format!(
54            "Array/Span couldn't be initialized from `{}`.",
55            type_path,
56        )))
57    }
58
59    pub fn apply_alias(&mut self, type_path: &str, alias: &str) {
60        self.inner.apply_alias(type_path, alias);
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67    use crate::tokens::*;
68
69    #[test]
70    fn test_parse() {
71        assert_eq!(
72            Array::parse("core::array::Array::<core::felt252>").unwrap(),
73            Array {
74                type_path: "core::array::Array::<core::felt252>".to_string(),
75                inner: Box::new(Token::CoreBasic(CoreBasic {
76                    type_path: "core::felt252".to_string()
77                })),
78                is_legacy: false,
79            }
80        );
81    }
82
83    #[test]
84    fn test_parse_no_inner_invalid() {
85        assert!(Array::parse("core::array::Array").is_err());
86        assert!(Array::parse("core::array::Array<>").is_err());
87    }
88
89    #[test]
90    fn test_parse_wrong_path_invalid() {
91        assert!(Array::parse("array::Array::<core::felt252>").is_err());
92    }
93
94    #[test]
95    fn test_parse_invalid_path_invalid() {
96        assert!(Array::parse("module::module2::array::Array::<core::felt252>").is_err());
97        assert!(Array::parse("module::module2::MyStruct::<core::felt252>").is_err());
98    }
99}