cainome_parser/tokens/
mod.rs

1//! Cairo ABI tokens.
2//!
3//! TODO.
4
5mod array;
6mod basic;
7mod composite;
8mod constants;
9mod function;
10mod genericity;
11mod tuple;
12
13use std::collections::HashMap;
14
15pub use array::Array;
16pub use basic::CoreBasic;
17pub use composite::{Composite, CompositeInner, CompositeInnerKind, CompositeType};
18pub use function::{Function, FunctionOutputKind, StateMutability};
19pub use tuple::Tuple;
20
21use crate::{CainomeResult, Error};
22
23#[derive(Debug, Clone, PartialEq)]
24pub enum Token {
25    CoreBasic(CoreBasic),
26    Array(Array),
27    Tuple(Tuple),
28    Composite(Composite),
29    GenericArg(String),
30    Function(Function),
31}
32
33impl Token {
34    pub fn parse(type_path: &str) -> CainomeResult<Self> {
35        if let Ok(b) = CoreBasic::parse(type_path) {
36            return Ok(Token::CoreBasic(b));
37        }
38
39        if let Ok(a) = Array::parse(type_path) {
40            return Ok(Token::Array(a));
41        }
42
43        if let Ok(t) = Tuple::parse(type_path) {
44            return Ok(Token::Tuple(t));
45        }
46
47        if let Ok(c) = Composite::parse(type_path) {
48            return Ok(Token::Composite(c));
49        }
50
51        Err(Error::TokenInitFailed(format!(
52            "Couldn't initialize a Token from type path `{}`",
53            type_path,
54        )))
55    }
56
57    pub fn type_name(&self) -> String {
58        match self {
59            Token::CoreBasic(t) => t.type_name(),
60            Token::Array(_) => "array".to_string(),
61            Token::Tuple(_) => "tuple".to_string(),
62            Token::Composite(t) => t.type_name(),
63            Token::GenericArg(_) => "generic_arg".to_string(),
64            Token::Function(_) => "function".to_string(),
65        }
66    }
67
68    pub fn type_path(&self) -> String {
69        match self {
70            Token::CoreBasic(t) => t.type_path.to_string(),
71            Token::Array(t) => t.type_path.to_string(),
72            Token::Tuple(t) => t.type_path.to_string(),
73            Token::Composite(t) => t.type_path_no_generic(),
74            Token::GenericArg(_) => "generic".to_string(),
75            Token::Function(t) => t.name.clone(),
76        }
77    }
78
79    // TODO: we may remove these two functions...! And change types somewhere..
80    pub fn to_composite(&self) -> CainomeResult<&Composite> {
81        match self {
82            Token::Composite(t) => Ok(t),
83            _ => Err(Error::ConversionFailed(format!(
84                "Can't convert token into composite, got {:?}",
85                self
86            ))),
87        }
88    }
89
90    pub fn to_function(&self) -> CainomeResult<&Function> {
91        match self {
92            Token::Function(t) => Ok(t),
93            _ => Err(Error::ConversionFailed(format!(
94                "Can't convert token into function, got {:?}",
95                self
96            ))),
97        }
98    }
99
100    pub fn resolve_generic(&self, generic_name: &str, generic_type_path: &str) -> Self {
101        match self {
102            Token::CoreBasic(t) => {
103                if t.type_path == generic_type_path {
104                    Token::GenericArg(generic_name.to_string())
105                } else {
106                    self.clone()
107                }
108            }
109            Token::Array(t) => t.resolve_generic(generic_name, generic_type_path),
110            Token::Tuple(t) => t.resolve_generic(generic_name, generic_type_path),
111            Token::Composite(t) => t.resolve_generic(generic_name, generic_type_path),
112            Token::GenericArg(_) => self.clone(),
113            Token::Function(_) => self.clone(),
114        }
115    }
116
117    pub fn apply_alias(&mut self, type_path: &str, alias: &str) {
118        match self {
119            Token::Array(t) => t.apply_alias(type_path, alias),
120            Token::Tuple(t) => t.apply_alias(type_path, alias),
121            Token::Composite(t) => t.apply_alias(type_path, alias),
122            Token::Function(t) => t.apply_alias(type_path, alias),
123            _ => (),
124        }
125    }
126
127    /// Recursively hydrates nested tokens
128    ///
129    /// Once abi is parsed, a flat list of tokens defined in cairo code is generated from parsed
130    /// json abi string.
131    /// Then token list are filtered to only keep single copy of each token.
132    /// Some tokens can have nested tokens that may not have inners defined inside thus leading to
133    /// confusion while using tokens. i.e Enums does not have inner variants defined.
134    ///
135    /// # Arguments
136    ///
137    /// * `token` - The token to hydrate.
138    /// * `filtered` - A map of type path to token that have already been hydrated.
139    /// * `recursion_max_depth` - Max depth recursion for token to hydrate.
140    /// * `iteration_count` - Current iteration count.
141    ///
142    pub fn hydrate(
143        token: Self,
144        filtered: &HashMap<String, Token>,
145        recursion_max_depth: usize,
146        iteration_count: usize,
147    ) -> Self {
148        if recursion_max_depth < iteration_count {
149            return token;
150        }
151        match token {
152            Token::CoreBasic(_) | Token::GenericArg(_) => token,
153            Token::Array(arr) => Token::Array(Array {
154                inner: Box::new(Self::hydrate(
155                    *arr.inner,
156                    filtered,
157                    recursion_max_depth,
158                    iteration_count + 1,
159                )),
160                type_path: arr.type_path,
161                is_legacy: arr.is_legacy,
162            }),
163            Token::Tuple(tup) => Token::Tuple(Tuple {
164                inners: tup
165                    .inners
166                    .into_iter()
167                    .map(|inner| {
168                        Self::hydrate(inner, filtered, recursion_max_depth, iteration_count + 1)
169                    })
170                    .collect(),
171                type_path: tup.type_path,
172            }),
173            Token::Composite(comp) => {
174                let type_path = comp.type_path_no_generic();
175
176                if comp.r#type == CompositeType::Unknown && !comp.is_builtin() {
177                    if let Some(hydrated) = filtered.get(&type_path) {
178                        return Token::hydrate(
179                            hydrated.clone(),
180                            filtered,
181                            recursion_max_depth,
182                            iteration_count + 1,
183                        );
184                    } else {
185                        panic!("Composite {} not found in filtered tokens", type_path);
186                    }
187                }
188                Token::Composite(Composite {
189                    type_path,
190                    inners: comp
191                        .inners
192                        .into_iter()
193                        .map(|i| CompositeInner {
194                            index: i.index,
195                            name: i.name,
196                            kind: i.kind,
197                            token: Self::hydrate(
198                                i.token,
199                                filtered,
200                                recursion_max_depth,
201                                iteration_count + 1,
202                            ),
203                        })
204                        .collect(),
205                    generic_args: comp
206                        .generic_args
207                        .into_iter()
208                        .map(|(name, token)| {
209                            (
210                                name,
211                                Self::hydrate(
212                                    token,
213                                    filtered,
214                                    recursion_max_depth,
215                                    iteration_count + 1,
216                                ),
217                            )
218                        })
219                        .collect(),
220                    r#type: comp.r#type,
221                    is_event: comp.is_event,
222                    alias: comp.alias,
223                })
224            }
225            Token::Function(func) => Token::Function(Function {
226                name: func.name,
227                inputs: func
228                    .inputs
229                    .into_iter()
230                    .map(|(name, token)| {
231                        (
232                            name,
233                            Self::hydrate(
234                                token,
235                                filtered,
236                                recursion_max_depth,
237                                iteration_count + 1,
238                            ),
239                        )
240                    })
241                    .collect(),
242                outputs: func
243                    .outputs
244                    .into_iter()
245                    .map(|token| {
246                        Self::hydrate(token, filtered, recursion_max_depth, iteration_count + 1)
247                    })
248                    .collect(),
249                named_outputs: func
250                    .named_outputs
251                    .into_iter()
252                    .map(|(name, token)| {
253                        (
254                            name,
255                            Self::hydrate(
256                                token,
257                                filtered,
258                                recursion_max_depth,
259                                iteration_count + 1,
260                            ),
261                        )
262                    })
263                    .collect(),
264                state_mutability: func.state_mutability,
265            }),
266        }
267    }
268}