1use alloc::string::{String, ToString};
2use core::fmt;
3use parser::TypeSpecifier;
4use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
14pub enum InternalType {
15 AddressPayable(String),
17 Contract(String),
19 Enum {
21 contract: Option<String>,
23 ty: String,
25 },
26 Struct {
28 contract: Option<String>,
30 ty: String,
32 },
33 Other {
35 contract: Option<String>,
37 ty: String,
39 },
40}
41
42impl fmt::Display for InternalType {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 self.as_borrowed().fmt(f)
45 }
46}
47
48impl Serialize for InternalType {
49 #[inline]
50 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
51 self.as_borrowed().serialize(serializer)
52 }
53}
54
55impl<'de> Deserialize<'de> for InternalType {
56 #[inline]
57 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
58 deserializer.deserialize_str(ItVisitor)
59 }
60}
61
62impl InternalType {
63 #[inline]
65 pub fn parse(s: &str) -> Option<Self> {
66 BorrowedInternalType::parse(s).map(BorrowedInternalType::into_owned)
67 }
68
69 #[inline]
71 pub const fn is_struct(&self) -> bool {
72 matches!(self, Self::Struct { .. })
73 }
74
75 #[inline]
77 pub const fn is_enum(&self) -> bool {
78 matches!(self, Self::Enum { .. })
79 }
80
81 #[inline]
83 pub const fn is_contract(&self) -> bool {
84 matches!(self, Self::Contract(_))
85 }
86
87 #[inline]
89 pub const fn is_address_payable(&self) -> bool {
90 matches!(self, Self::AddressPayable(_))
91 }
92
93 #[inline]
95 pub const fn is_other(&self) -> bool {
96 matches!(self, Self::Other { .. })
97 }
98
99 #[inline]
101 pub fn as_struct(&self) -> Option<(Option<&str>, &str)> {
102 match self {
103 Self::Struct { contract, ty } => Some((contract.as_deref(), ty)),
104 _ => None,
105 }
106 }
107
108 #[inline]
110 pub fn as_enum(&self) -> Option<(Option<&str>, &str)> {
111 match self {
112 Self::Enum { contract, ty } => Some((contract.as_deref(), ty)),
113 _ => None,
114 }
115 }
116
117 #[inline]
119 pub fn as_contract(&self) -> Option<&str> {
120 match self {
121 Self::Contract(s) => Some(s),
122 _ => None,
123 }
124 }
125
126 #[inline]
128 pub fn as_other(&self) -> Option<(Option<&str>, &str)> {
129 match self {
130 Self::Other { contract, ty } => Some((contract.as_deref(), ty)),
131 _ => None,
132 }
133 }
134
135 #[inline]
138 pub fn struct_specifier(&self) -> Option<TypeSpecifier<'_>> {
139 self.as_struct().and_then(|s| TypeSpecifier::parse(s.1).ok())
140 }
141
142 #[inline]
144 pub fn enum_specifier(&self) -> Option<TypeSpecifier<'_>> {
145 self.as_enum().and_then(|s| TypeSpecifier::parse(s.1).ok())
146 }
147
148 #[inline]
151 pub fn contract_specifier(&self) -> Option<TypeSpecifier<'_>> {
152 self.as_contract().and_then(|s| TypeSpecifier::parse(s).ok())
153 }
154
155 #[inline]
160 pub fn other_specifier(&self) -> Option<TypeSpecifier<'_>> {
161 self.as_other().and_then(|s| TypeSpecifier::parse(s.1).ok())
162 }
163
164 #[inline]
165 pub(crate) fn as_borrowed(&self) -> BorrowedInternalType<'_> {
166 match self {
167 Self::AddressPayable(s) => BorrowedInternalType::AddressPayable(s),
168 Self::Contract(s) => BorrowedInternalType::Contract(s),
169 Self::Enum { contract, ty } => {
170 BorrowedInternalType::Enum { contract: contract.as_deref(), ty }
171 }
172 Self::Struct { contract, ty } => {
173 BorrowedInternalType::Struct { contract: contract.as_deref(), ty }
174 }
175 Self::Other { contract, ty } => {
176 BorrowedInternalType::Other { contract: contract.as_deref(), ty }
177 }
178 }
179 }
180}
181
182#[derive(Clone, Copy, Debug)]
183pub(crate) enum BorrowedInternalType<'a> {
184 AddressPayable(&'a str),
185 Contract(&'a str),
186 Enum { contract: Option<&'a str>, ty: &'a str },
187 Struct { contract: Option<&'a str>, ty: &'a str },
188 Other { contract: Option<&'a str>, ty: &'a str },
189}
190
191impl fmt::Display for BorrowedInternalType<'_> {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 match *self {
194 Self::AddressPayable(s) => f.write_str(s),
195 Self::Contract(s) => {
196 f.write_str("contract ")?;
197 f.write_str(s)
198 }
199 Self::Enum { contract, ty }
200 | Self::Struct { contract, ty }
201 | Self::Other { contract, ty } => {
202 match self {
203 Self::Enum { .. } => f.write_str("enum ")?,
204 Self::Struct { .. } => f.write_str("struct ")?,
205 Self::Other { .. } => {}
206 _ => unreachable!(),
207 }
208 if let Some(c) = contract {
209 f.write_str(c)?;
210 f.write_str(".")?;
211 }
212 f.write_str(ty)
213 }
214 }
215 }
216}
217
218impl Serialize for BorrowedInternalType<'_> {
219 #[inline]
220 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
221 serializer.collect_str(self)
222 }
223}
224
225impl<'de: 'a, 'a> Deserialize<'de> for BorrowedInternalType<'a> {
226 #[inline]
227 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
228 deserializer.deserialize_str(BorrowedItVisitor)
229 }
230}
231
232impl<'a> BorrowedInternalType<'a> {
233 fn parse(v: &'a str) -> Option<Self> {
235 if v.starts_with("address payable") {
236 return Some(Self::AddressPayable(v));
237 }
238 if let Some(body) = v.strip_prefix("enum ") {
239 if let Some((contract, ty)) = body.split_once('.') {
240 Some(Self::Enum { contract: Some(contract), ty })
241 } else {
242 Some(Self::Enum { contract: None, ty: body })
243 }
244 } else if let Some(body) = v.strip_prefix("struct ") {
245 if let Some((contract, ty)) = body.split_once('.') {
246 Some(Self::Struct { contract: Some(contract), ty })
247 } else {
248 Some(Self::Struct { contract: None, ty: body })
249 }
250 } else if let Some(body) = v.strip_prefix("contract ") {
251 Some(Self::Contract(body))
252 } else if let Some((contract, ty)) = v.split_once('.') {
253 Some(Self::Other { contract: Some(contract), ty })
254 } else {
255 Some(Self::Other { contract: None, ty: v })
256 }
257 }
258
259 pub(crate) fn into_owned(self) -> InternalType {
260 match self {
261 Self::AddressPayable(s) => InternalType::AddressPayable(s.to_string()),
262 Self::Contract(s) => InternalType::Contract(s.to_string()),
263 Self::Enum { contract, ty } => {
264 InternalType::Enum { contract: contract.map(String::from), ty: ty.to_string() }
265 }
266 Self::Struct { contract, ty } => {
267 InternalType::Struct { contract: contract.map(String::from), ty: ty.to_string() }
268 }
269 Self::Other { contract, ty } => {
270 InternalType::Other { contract: contract.map(String::from), ty: ty.to_string() }
271 }
272 }
273 }
274}
275
276const VISITOR_EXPECTED: &str = "a valid internal type";
277
278pub(crate) struct ItVisitor;
279
280impl Visitor<'_> for ItVisitor {
281 type Value = InternalType;
282
283 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 f.write_str(VISITOR_EXPECTED)
285 }
286
287 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
288 BorrowedInternalType::parse(v)
289 .map(BorrowedInternalType::into_owned)
290 .ok_or_else(|| E::invalid_value(serde::de::Unexpected::Str(v), &VISITOR_EXPECTED))
291 }
292}
293
294const BORROWED_VISITOR_EXPECTED: &str = "a valid borrowed internal type";
295
296pub(crate) struct BorrowedItVisitor;
297
298impl<'de> Visitor<'de> for BorrowedItVisitor {
299 type Value = BorrowedInternalType<'de>;
300
301 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 f.write_str(BORROWED_VISITOR_EXPECTED)
303 }
304
305 fn visit_borrowed_str<E: serde::de::Error>(self, v: &'de str) -> Result<Self::Value, E> {
306 BorrowedInternalType::parse(v).ok_or_else(|| {
307 E::invalid_value(serde::de::Unexpected::Str(v), &BORROWED_VISITOR_EXPECTED)
308 })
309 }
310
311 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
312 Err(E::invalid_value(serde::de::Unexpected::Str(v), &BORROWED_VISITOR_EXPECTED))
313 }
314}
315
316#[cfg(test)]
317mod test {
318 use super::*;
319
320 macro_rules! parser_test {
321 ($test_str:expr, $expected:expr) => {
322 assert_eq!(InternalType::parse($test_str).unwrap(), $expected);
323 };
324 }
325
326 #[test]
327 fn parse_simple_internal_types() {
328 parser_test!(
329 "struct SpentItem[]",
330 InternalType::Struct { contract: None, ty: "SpentItem[]".into() }
331 );
332 parser_test!(
333 "struct Contract.Item",
334 InternalType::Struct { contract: Some("Contract".into()), ty: "Item".into() }
335 );
336 parser_test!(
337 "enum ItemType[32]",
338 InternalType::Enum { contract: None, ty: "ItemType[32]".into() }
339 );
340 parser_test!(
341 "enum Contract.Item",
342 InternalType::Enum { contract: Some("Contract".into()), ty: "Item".into() }
343 );
344
345 parser_test!("contract Item", InternalType::Contract("Item".into()));
346 parser_test!("contract Item[]", InternalType::Contract("Item[]".into()));
347 parser_test!("contract Item[][2]", InternalType::Contract("Item[][2]".into()));
348 parser_test!("contract Item[][2][]", InternalType::Contract("Item[][2][]".into()));
349
350 parser_test!(
351 "address payable",
352 InternalType::AddressPayable("address payable".to_string())
353 );
354 parser_test!(
355 "address payable[][][][][]",
356 InternalType::AddressPayable("address payable[][][][][]".into())
357 );
358 parser_test!("Item", InternalType::Other { contract: None, ty: "Item".into() });
359 parser_test!(
360 "Contract.Item[][33]",
361 InternalType::Other { contract: Some("Contract".into()), ty: "Item[][33]".into() }
362 );
363 }
364}