1use super::constants::{CAIRO_COMPOSITE_BUILTINS, CAIRO_GENERIC_BUILTINS};
62use super::genericity;
63use super::Token;
64
65use crate::CainomeResult;
66
67#[derive(Debug, Copy, Clone, PartialEq)]
68pub enum CompositeType {
69 Struct,
70 Enum,
71 Unknown,
72}
73
74#[derive(Debug, Copy, Clone, PartialEq)]
75pub enum CompositeInnerKind {
76 Key,
77 Data,
78 Nested,
79 Flat,
80 NotUsed,
81}
82
83#[derive(Debug, Clone, PartialEq)]
84pub struct CompositeInner {
85 pub index: usize,
86 pub name: String,
87 pub kind: CompositeInnerKind,
88 pub token: Token,
89}
90
91#[derive(Debug, Clone, PartialEq)]
92pub struct Composite {
93 pub type_path: String,
94 pub inners: Vec<CompositeInner>,
95 pub generic_args: Vec<(String, Token)>,
96 pub r#type: CompositeType,
97 pub is_event: bool,
98 pub alias: Option<String>,
99}
100
101impl Composite {
102 pub fn parse(type_path: &str) -> CainomeResult<Self> {
122 let type_path = escape_rust_keywords(type_path);
123 let generic_args = genericity::extract_generics_args(&type_path)?;
124
125 Ok(Self {
126 type_path: type_path.to_string(),
128 inners: vec![],
129 generic_args,
130 r#type: CompositeType::Unknown,
131 is_event: false,
132 alias: None,
133 })
134 }
135
136 pub fn type_path_no_generic(&self) -> String {
137 genericity::type_path_no_generic(&self.type_path)
138 }
139
140 pub fn is_generic(&self) -> bool {
141 !self.generic_args.is_empty()
142 }
143
144 pub fn is_builtin(&self) -> bool {
149 for b in CAIRO_GENERIC_BUILTINS {
150 if self.type_path.starts_with(b) {
151 return true;
152 }
153 }
154
155 for b in CAIRO_COMPOSITE_BUILTINS {
156 if self.type_path.starts_with(b) {
157 return true;
158 }
159 }
160
161 false
162 }
163
164 pub fn type_name(&self) -> String {
165 extract_type_path_with_depth(&self.type_path_no_generic(), 0)
167 }
168
169 pub fn type_name_or_alias(&self) -> String {
170 if let Some(a) = &self.alias {
171 a.clone()
172 } else {
173 self.type_name()
174 }
175 }
176
177 pub fn apply_alias(&mut self, type_path: &str, alias: &str) {
178 if self.type_path_no_generic() == type_path {
179 self.alias = Some(alias.to_string());
180 }
181
182 for ref mut i in &mut self.inners {
183 if let Token::Composite(ref mut c) = i.token {
184 c.apply_alias(type_path, alias);
185 }
186 }
187 }
188}
189
190pub fn snake_to_pascal_case(s: &str) -> String {
192 s.split('_')
193 .map(|word| {
194 let mut c = word.chars();
195 match c.next() {
196 None => String::new(),
197 Some(f) => f.to_uppercase().collect::<String>() + c.as_str(),
198 }
199 })
200 .collect()
201}
202
203pub fn escape_rust_keywords(s: &str) -> String {
205 let keywords = ["move", "type", "final"];
206
207 let mut s = s.to_string();
208
209 for k in keywords {
210 let k_start = format!("{k}::");
211 let k_middle = format!("::{k}::");
212 let k_end = format!("::{k}");
213
214 if s == k {
215 return format!("r#{k}");
216 } else if s.starts_with(&k_start) {
217 s = s.replace(&k_start, &format!("r#{k}::"));
218 } else if s.ends_with(&k_end) {
219 s = s.replace(&k_end, &format!("::r#{k}"));
220 } else {
221 s = s.replace(&k_middle, &format!("::r#{k}::"));
222 }
223 }
224
225 s
226}
227
228pub fn extract_type_path_with_depth(type_path: &str, depth: usize) -> String {
242 let segments: Vec<&str> = type_path.split("::").collect();
243
244 let mut depth = depth;
245 if segments.len() < depth + 1 {
246 depth = segments.len() - 1;
247 }
248
249 let segments = &segments[segments.len() - depth - 1..segments.len()];
250 segments.iter().map(|s| snake_to_pascal_case(s)).collect()
251}
252
253#[cfg(test)]
254mod tests {
255 use super::*;
256 use crate::tokens::*;
257
258 fn basic_felt252() -> Token {
259 Token::CoreBasic(CoreBasic {
260 type_path: "core::felt252".to_string(),
261 })
262 }
263
264 fn basic_u64() -> Token {
265 Token::CoreBasic(CoreBasic {
266 type_path: "core::integer::u64".to_string(),
267 })
268 }
269
270 #[test]
271 fn test_snake_to_pascal_case() {
272 assert_eq!(snake_to_pascal_case("my_type"), "MyType");
273 assert_eq!(snake_to_pascal_case("my_type_long"), "MyTypeLong");
274 assert_eq!(snake_to_pascal_case("type"), "Type");
275 assert_eq!(snake_to_pascal_case("MyType"), "MyType");
276 assert_eq!(snake_to_pascal_case("MyType_hybrid"), "MyTypeHybrid");
277 assert_eq!(snake_to_pascal_case(""), "");
278 }
279
280 #[test]
281 fn test_extract_type_with_depth() {
282 assert_eq!(extract_type_path_with_depth("type_name", 0), "TypeName");
283 assert_eq!(extract_type_path_with_depth("type_name", 10), "TypeName");
284 assert_eq!(
285 extract_type_path_with_depth("module::TypeName", 1),
286 "ModuleTypeName"
287 );
288 assert_eq!(
289 extract_type_path_with_depth("module::TypeName", 8),
290 "ModuleTypeName"
291 );
292 assert_eq!(
293 extract_type_path_with_depth("module_one::module_1::TypeName", 2),
294 "ModuleOneModule1TypeName"
295 );
296 }
297
298 #[test]
299 fn test_parse() {
300 let expected = Composite {
301 type_path: "module::MyStruct".to_string(),
302 inners: vec![],
303 generic_args: vec![],
304 r#type: CompositeType::Unknown,
305 is_event: false,
306 alias: None,
307 };
308
309 assert_eq!(Composite::parse("module::MyStruct").unwrap(), expected);
310 assert!(!expected.is_generic());
311 }
312
313 #[test]
314 fn test_parse_generic_one() {
315 let expected = Composite {
316 type_path: "module::MyStruct::<core::felt252>".to_string(),
317 inners: vec![],
318 generic_args: vec![("A".to_string(), basic_felt252())],
319 r#type: CompositeType::Unknown,
320 is_event: false,
321 alias: None,
322 };
323
324 assert_eq!(
325 Composite::parse("module::MyStruct::<core::felt252>").unwrap(),
326 expected
327 );
328 assert!(expected.is_generic());
329 }
330
331 #[test]
332 fn test_parse_generic_two() {
333 let expected = Composite {
334 type_path: "module::MyStruct::<core::felt252, core::integer::u64>".to_string(),
335 inners: vec![],
336 generic_args: vec![
337 ("A".to_string(), basic_felt252()),
338 ("B".to_string(), basic_u64()),
339 ],
340 r#type: CompositeType::Unknown,
341 is_event: false,
342 alias: None,
343 };
344
345 assert_eq!(
346 Composite::parse("module::MyStruct::<core::felt252, core::integer::u64>").unwrap(),
347 expected
348 );
349 assert!(expected.is_generic());
350 }
351
352 #[test]
353 fn test_type_name() {
354 let mut c = Composite {
355 type_path: "module::MyStruct".to_string(),
356 inners: vec![],
357 generic_args: vec![],
358 r#type: CompositeType::Unknown,
359 is_event: false,
360 alias: None,
361 };
362 assert_eq!(c.type_name(), "MyStruct");
363
364 c.type_path = "module::MyStruct::<core::felt252>".to_string();
365 assert_eq!(c.type_name(), "MyStruct");
366 }
367
368 #[test]
369 fn test_escape_rust_keywords() {
370 assert_eq!(escape_rust_keywords("move"), "r#move",);
371
372 assert_eq!(escape_rust_keywords("move::salut"), "r#move::salut",);
373
374 assert_eq!(escape_rust_keywords("hey::move"), "hey::r#move",);
375
376 assert_eq!(
377 escape_rust_keywords("hey::move::salut"),
378 "hey::r#move::salut",
379 );
380
381 assert_eq!(
382 escape_rust_keywords("type::move::final"),
383 "r#type::r#move::r#final",
384 );
385 }
386}