cainome_parser/abi/
conversions.rs

1use starknet::core::types::contract::{
2    legacy::{RawLegacyEvent, RawLegacyStruct},
3    AbiEnum, AbiEventEnum, AbiEventStruct, AbiStruct, EventFieldKind,
4    StateMutability as StarknetStateMutability,
5};
6
7use crate::tokens::{CompositeInner, CompositeInnerKind, CompositeType, StateMutability, Token};
8use crate::Error;
9
10impl From<StarknetStateMutability> for StateMutability {
11    fn from(value: StarknetStateMutability) -> Self {
12        match value {
13            StarknetStateMutability::External => StateMutability::External,
14            StarknetStateMutability::View => StateMutability::View,
15        }
16    }
17}
18
19impl From<EventFieldKind> for CompositeInnerKind {
20    fn from(value: EventFieldKind) -> Self {
21        match value {
22            EventFieldKind::Key => CompositeInnerKind::Key,
23            EventFieldKind::Data => CompositeInnerKind::Data,
24            EventFieldKind::Nested => CompositeInnerKind::Nested,
25            EventFieldKind::Flat => CompositeInnerKind::Flat,
26        }
27    }
28}
29
30impl TryFrom<&AbiStruct> for Token {
31    type Error = Error;
32
33    fn try_from(value: &AbiStruct) -> Result<Self, Self::Error> {
34        let mut t = Token::parse(&value.name)?;
35
36        if let Token::Composite(ref mut c) = t {
37            c.r#type = CompositeType::Struct;
38
39            for (i, m) in value.members.iter().enumerate() {
40                c.inners.push(CompositeInner {
41                    index: i,
42                    name: m.name.clone(),
43                    token: Token::parse(&m.r#type).unwrap(),
44                    kind: CompositeInnerKind::NotUsed,
45                });
46            }
47
48            Ok(t)
49        } else {
50            Err(Error::ParsingFailed(format!(
51                "AbiStruct is expected to be a Composite token, got `{:?}`",
52                value,
53            )))
54        }
55    }
56}
57
58impl TryFrom<&AbiEnum> for Token {
59    type Error = Error;
60
61    fn try_from(value: &AbiEnum) -> Result<Self, Self::Error> {
62        let mut t = Token::parse(&value.name)?;
63
64        if t.type_name() == "option" {
65            return Ok(t);
66        }
67
68        if t.type_name() == "result" {
69            return Ok(t);
70        }
71
72        if let Token::Composite(ref mut c) = t {
73            c.r#type = CompositeType::Enum;
74
75            for (i, v) in value.variants.iter().enumerate() {
76                // Determine the kind based on whether the variant has data
77                let kind = if v.r#type == "()" {
78                    CompositeInnerKind::NotUsed
79                } else {
80                    CompositeInnerKind::Data
81                };
82
83                c.inners.push(CompositeInner {
84                    index: i,
85                    name: v.name.clone(),
86                    token: Token::parse(&v.r#type).unwrap(),
87                    kind,
88                });
89            }
90
91            Ok(t)
92        } else {
93            Err(Error::ParsingFailed(format!(
94                "AbiEnum is expected to be a Composite token, got `{:?}`",
95                value,
96            )))
97        }
98    }
99}
100
101impl TryFrom<&AbiEventStruct> for Token {
102    type Error = Error;
103
104    fn try_from(value: &AbiEventStruct) -> Result<Self, Self::Error> {
105        let mut t = Token::parse(&value.name)?;
106
107        if let Token::Composite(ref mut c) = t {
108            c.r#type = CompositeType::Struct;
109            c.is_event = true;
110
111            for (i, m) in value.members.iter().enumerate() {
112                c.inners.push(CompositeInner {
113                    index: i,
114                    name: m.name.clone(),
115                    token: Token::parse(&m.r#type).unwrap(),
116                    kind: m.kind.clone().into(),
117                });
118            }
119
120            Ok(t)
121        } else {
122            Err(Error::ParsingFailed(format!(
123                "AbiEventStruct is expected to be a Composite token, got `{:?}`",
124                value,
125            )))
126        }
127    }
128}
129
130impl TryFrom<&AbiEventEnum> for Token {
131    type Error = Error;
132
133    fn try_from(value: &AbiEventEnum) -> Result<Self, Self::Error> {
134        let mut t = Token::parse(&value.name)?;
135
136        if let Token::Composite(ref mut c) = t {
137            c.r#type = CompositeType::Enum;
138            c.is_event = true;
139
140            for (i, v) in value.variants.iter().enumerate() {
141                c.inners.push(CompositeInner {
142                    index: i,
143                    name: v.name.clone(),
144                    token: Token::parse(&v.r#type).unwrap(),
145                    kind: v.kind.clone().into(),
146                });
147            }
148
149            Ok(t)
150        } else {
151            Err(Error::ParsingFailed(format!(
152                "AbiEventEnum is expected to be a Composite token, got `{:?}`",
153                value,
154            )))
155        }
156    }
157}
158
159impl TryFrom<&RawLegacyStruct> for Token {
160    type Error = Error;
161
162    fn try_from(value: &RawLegacyStruct) -> Result<Self, Self::Error> {
163        let mut t = Token::parse(&value.name)?;
164
165        if let Token::Composite(ref mut c) = t {
166            c.r#type = CompositeType::Struct;
167
168            for (i, m) in value.members.iter().enumerate() {
169                c.inners.push(CompositeInner {
170                    index: i,
171                    name: m.name.clone(),
172                    token: Token::parse(&m.r#type).unwrap(),
173                    kind: CompositeInnerKind::NotUsed,
174                });
175            }
176
177            Ok(t)
178        } else {
179            Err(Error::ParsingFailed(format!(
180                "RawLegacyStruct is expected to be a Composite token, got `{:?}`",
181                value,
182            )))
183        }
184    }
185}
186
187impl TryFrom<&RawLegacyEvent> for Token {
188    type Error = Error;
189
190    fn try_from(value: &RawLegacyEvent) -> Result<Self, Self::Error> {
191        let mut t = Token::parse(&value.name)?;
192
193        if let Token::Composite(ref mut c) = t {
194            c.r#type = CompositeType::Struct;
195            c.is_event = true;
196
197            let mut i = 0;
198
199            for m in value.data.iter() {
200                c.inners.push(CompositeInner {
201                    index: i,
202                    name: m.name.clone(),
203                    token: Token::parse(&m.r#type).unwrap(),
204                    kind: CompositeInnerKind::Data,
205                });
206
207                i += 1;
208            }
209
210            for m in value.keys.iter() {
211                c.inners.push(CompositeInner {
212                    index: i,
213                    name: m.name.clone(),
214                    token: Token::parse(&m.r#type).unwrap(),
215                    kind: CompositeInnerKind::Key,
216                });
217
218                i += 1;
219            }
220
221            Ok(t)
222        } else {
223            Err(Error::ParsingFailed(format!(
224                "RawLegacyEvent is expected to be a Composite token, got `{:?}`",
225                value,
226            )))
227        }
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use crate::tokens::CompositeType;
235    use crate::AbiParser;
236    use std::collections::HashMap;
237
238    #[test]
239    fn test_enum_variant_composite_inner_kind() {
240        // Test ABI with enum variants - some with data, some without
241        let abi_json = r#"
242        [
243            {
244                "type": "enum",
245                "name": "test::TestEnum",
246                "variants": [
247                    {
248                        "name": "VariantWithoutData",
249                        "type": "()"
250                    },
251                    {
252                        "name": "VariantWithFelt252",
253                        "type": "core::felt252"
254                    },
255                    {
256                        "name": "VariantWithTuple",
257                        "type": "(core::felt252, core::integer::u32)"
258                    }
259                ]
260            }
261        ]
262        "#;
263
264        let result = AbiParser::tokens_from_abi_string(abi_json, &HashMap::new()).unwrap();
265
266        assert_eq!(result.enums.len(), 1);
267        let enum_composite = result.enums[0].to_composite().unwrap();
268
269        assert_eq!(enum_composite.r#type, CompositeType::Enum);
270        assert_eq!(enum_composite.inners.len(), 3);
271
272        // Check that variant without data has NotUsed kind
273        assert_eq!(enum_composite.inners[0].name, "VariantWithoutData");
274        assert_eq!(enum_composite.inners[0].kind, CompositeInnerKind::NotUsed);
275
276        // Check that variant with felt252 has Data kind
277        assert_eq!(enum_composite.inners[1].name, "VariantWithFelt252");
278        assert_eq!(enum_composite.inners[1].kind, CompositeInnerKind::Data);
279
280        // Check that variant with tuple has Data kind
281        assert_eq!(enum_composite.inners[2].name, "VariantWithTuple");
282        assert_eq!(enum_composite.inners[2].kind, CompositeInnerKind::Data);
283    }
284}