1use crate::abi::{
3 error::{bail, format_err, Result},
4 human_readable::{is_whitespace, parse_identifier},
5 HumanReadableParser, ParamType,
6};
7
8#[derive(Debug, Clone, PartialEq)]
10pub struct FieldDeclaration {
11 pub name: String,
12 pub ty: FieldType,
13}
14
15impl FieldDeclaration {
16 pub fn new(name: String, ty: FieldType) -> Self {
17 Self { name, ty }
18 }
19
20 pub fn name(&self) -> &str {
21 &self.name
22 }
23
24 pub fn r#type(&self) -> &FieldType {
25 &self.ty
26 }
27}
28
29#[derive(Debug, Clone, PartialEq)]
31pub enum FieldType {
32 Elementary(ParamType),
36 Struct(StructFieldType),
38 Mapping(Box<MappingType>),
40}
41
42impl FieldType {
43 pub fn is_elementary(&self) -> bool {
45 matches!(self, FieldType::Elementary(_))
46 }
47
48 pub fn is_struct(&self) -> bool {
50 matches!(self, FieldType::Struct(_))
51 }
52
53 pub fn is_mapping(&self) -> bool {
55 matches!(self, FieldType::Mapping(_))
56 }
57
58 pub(crate) fn as_struct(&self) -> Option<&StructFieldType> {
59 match self {
60 FieldType::Struct(s) => Some(s),
61 _ => None,
62 }
63 }
64}
65
66#[derive(Debug, Clone, PartialEq)]
67pub struct MappingType {
68 key_type: ParamType,
73 value_type: FieldType,
75}
76
77#[derive(Debug, Clone, PartialEq)]
79pub struct StructFieldDeclaration {
80 name: String,
82 ty: StructFieldType,
84}
85
86#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct StructType {
89 name: String,
91 projections: Vec<String>,
95}
96
97impl StructType {
98 pub fn new(name: String, projections: Vec<String>) -> Self {
99 Self { name, projections }
100 }
101
102 pub fn name(&self) -> &str {
103 &self.name
104 }
105}
106
107#[derive(Debug, Clone, PartialEq)]
109pub enum StructFieldType {
110 Type(StructType),
112 Array(Box<StructFieldType>),
114 FixedArray(Box<StructFieldType>, usize),
116}
117
118impl StructFieldType {
119 pub fn name(&self) -> &str {
120 match self {
121 StructFieldType::Type(ty) => &ty.name,
122 StructFieldType::Array(ty) => ty.name(),
123 StructFieldType::FixedArray(ty, _) => ty.name(),
124 }
125 }
126
127 pub fn projections(&self) -> &[String] {
128 match self {
129 StructFieldType::Type(ty) => &ty.projections,
130 StructFieldType::Array(ty) => ty.projections(),
131 StructFieldType::FixedArray(ty, _) => ty.projections(),
132 }
133 }
134
135 pub fn identifier(&self) -> String {
136 let name = self.name();
137 let path = self.projections().join(".");
138 if path.is_empty() {
139 name.to_string()
140 } else {
141 format!("{path}.{name}")
142 }
143 }
144
145 pub fn as_param(&self, tuple: ParamType) -> ParamType {
146 match self {
147 StructFieldType::Type(_) => tuple,
148 StructFieldType::Array(ty) => ty.as_param(ParamType::Array(Box::new(tuple))),
149 StructFieldType::FixedArray(ty, size) => {
150 ty.as_param(ParamType::FixedArray(Box::new(tuple), *size))
151 }
152 }
153 }
154
155 pub fn parse(mut input: &str) -> Result<FieldType> {
159 let mut projections = Vec::new();
160
161 loop {
162 let ty = parse_identifier(&mut input)?;
163 let mut chars = input.chars();
164 match chars.next() {
165 None => {
166 return Ok(FieldType::Struct(StructFieldType::Type(StructType {
167 name: ty,
168 projections,
169 })))
170 }
171 Some(' ') | Some('\t') | Some('[') => {
172 let mut size = String::new();
174 loop {
175 match chars.next() {
176 None => bail!("Expected Array `{}`", input),
177 Some(' ') | Some('\t') => {
178 if !size.is_empty() {
179 bail!(
180 "Illegal whitespace in array size after `{}` in `{}`",
181 size,
182 input
183 )
184 }
185 }
186 Some(']') => {
187 let ty = StructType { name: ty, projections };
188
189 return if size.is_empty() {
190 Ok(FieldType::Struct(StructFieldType::Array(Box::new(
191 StructFieldType::Type(ty),
192 ))))
193 } else {
194 let size = size.parse().map_err(|_| {
195 format_err!("Illegal array size `{}` at `{}`", size, input)
196 })?;
197 Ok(FieldType::Struct(StructFieldType::FixedArray(
198 Box::new(StructFieldType::Type(ty)),
199 size,
200 )))
201 }
202 }
203 Some(c) => {
204 if c.is_numeric() {
205 size.push(c);
206 } else {
207 bail!("Illegal char `{}` inner array `{}`", c, input)
208 }
209 }
210 }
211 }
212 }
213 Some('.') => {
214 input = chars.as_str();
215 projections.push(ty);
216 }
217 Some(c) => {
218 bail!("Illegal char `{}` at `{}`", c, input)
219 }
220 }
221 }
222 }
223}
224
225#[derive(Debug, Clone, PartialEq)]
227pub struct SolStruct {
228 pub name: String,
229 pub fields: Vec<FieldDeclaration>,
230}
231
232impl SolStruct {
233 pub fn parse(s: &str) -> Result<Self> {
242 let mut input = s.trim();
243 if !input.starts_with("struct ") {
244 bail!("Not a struct `{}`", input)
245 }
246 input = &input[6..];
247
248 let name = parse_identifier(&mut input)?;
249
250 let mut chars = input.chars();
251
252 loop {
253 match chars.next() {
254 None => bail!("Expected struct"),
255 Some('{') => {
256 input = chars
258 .as_str()
259 .trim()
260 .strip_suffix('}')
261 .ok_or_else(|| format_err!("Expected closing `}}` in `{}`", s))?
262 .trim_end();
263
264 let fields = if input.is_empty() {
265 Vec::new()
266 } else {
267 input
268 .split(';')
269 .filter(|s| !s.is_empty())
270 .map(parse_struct_field)
271 .collect::<Result<Vec<_>, _>>()?
272 };
273 return Ok(SolStruct { name, fields })
274 }
275 Some(' ') | Some('\t') => continue,
276 Some(c) => {
277 bail!("Illegal char `{}` at `{}`", c, s)
278 }
279 }
280 }
281 }
282
283 pub fn name(&self) -> &str {
285 &self.name
286 }
287
288 pub fn fields(&self) -> &Vec<FieldDeclaration> {
290 &self.fields
291 }
292
293 pub fn has_nameless_field(&self) -> bool {
295 self.fields.iter().any(|f| f.name.is_empty())
296 }
297
298 pub fn as_tuple(&self) -> Option<ParamType> {
301 let mut params = Vec::with_capacity(self.fields.len());
302 for field in self.fields() {
303 if let FieldType::Elementary(ref param) = field.ty {
304 params.push(param.clone())
305 } else {
306 return None
307 }
308 }
309 Some(ParamType::Tuple(params))
310 }
311}
312
313fn strip_field_identifier(input: &mut &str) -> Result<String> {
315 let mut iter = input.trim_end().rsplitn(2, is_whitespace);
316 let name = iter
317 .next()
318 .ok_or_else(|| format_err!("Expected field identifier"))
319 .map(|mut s| parse_identifier(&mut s))??;
320 *input =
321 iter.next().ok_or_else(|| format_err!("Expected field type in `{}`", input))?.trim_end();
322 Ok(name)
323}
324
325fn parse_struct_field(s: &str) -> Result<FieldDeclaration> {
327 let mut input = s.trim_start();
328
329 if !input.starts_with("mapping") {
330 input = input
332 .split('=')
333 .next()
334 .ok_or_else(|| format_err!("Expected field definition `{}`", s))?
335 .trim_end();
336 }
337 let name = strip_field_identifier(&mut input)?;
338 Ok(FieldDeclaration { name, ty: parse_field_type(input)? })
339}
340
341fn parse_field_type(s: &str) -> Result<FieldType> {
342 let mut input = s.trim_start();
343 if input.starts_with("mapping") {
344 return Ok(FieldType::Mapping(Box::new(parse_mapping(input)?)))
345 }
346 if input.ends_with(" payable") {
347 input = input[..input.len() - 7].trim_end();
349 }
350 if let Ok(ty) = HumanReadableParser::parse_type(input) {
351 Ok(FieldType::Elementary(ty))
352 } else {
353 StructFieldType::parse(input.trim_end())
355 }
356}
357
358fn parse_mapping(s: &str) -> Result<MappingType> {
360 let mut input = s.trim();
361 if !input.starts_with("mapping") {
362 bail!("Not a mapping `{}`", input)
363 }
364 input = input[7..].trim_start();
365 let mut iter = input.trim_start_matches('(').trim_end_matches(')').splitn(2, "=>");
366 let key_type = iter
367 .next()
368 .ok_or_else(|| format_err!("Expected mapping key type at `{}`", input))
369 .map(str::trim)
370 .map(HumanReadableParser::parse_type)??;
371
372 let is_illegal_ty = matches!(
373 &key_type,
374 ParamType::Array(_) | ParamType::FixedArray(_, _) | ParamType::Tuple(_)
375 );
376
377 if is_illegal_ty {
378 bail!("Expected elementary mapping key type at `{}` got {:?}", input, key_type)
379 }
380
381 let value_type = iter
382 .next()
383 .ok_or_else(|| format_err!("Expected mapping value type at `{}`", input))
384 .map(str::trim)
385 .map(parse_field_type)??;
386
387 Ok(MappingType { key_type, value_type })
388}
389
390#[cfg(test)]
391mod tests {
392 use super::*;
393
394 #[test]
395 fn can_parse_simple_struct() {
396 assert_eq!(
397 SolStruct::parse("struct MyStruct{uint256 x; uint256 y;}").unwrap(),
398 SolStruct {
399 name: "MyStruct".to_string(),
400 fields: vec![
401 FieldDeclaration {
402 name: "x".to_string(),
403 ty: FieldType::Elementary(ParamType::Uint(256)),
404 },
405 FieldDeclaration {
406 name: "y".to_string(),
407 ty: FieldType::Elementary(ParamType::Uint(256)),
408 },
409 ],
410 }
411 );
412 }
413
414 #[test]
415 fn can_parse_struct() {
416 assert_eq!(
417 SolStruct::parse("struct MyStruct{uint256 x; uint256 y; bytes[] _b; string[10] s; mapping(address => uint256) m;}").unwrap(),
418 SolStruct {
419 name: "MyStruct".to_string(),
420 fields: vec![
421 FieldDeclaration {
422 name: "x".to_string(),
423 ty: FieldType::Elementary(ParamType::Uint(256)),
424 },
425 FieldDeclaration {
426 name: "y".to_string(),
427 ty: FieldType::Elementary(ParamType::Uint(256)),
428 },
429 FieldDeclaration {
430 name: "_b".to_string(),
431 ty: FieldType::Elementary(ParamType::Array(Box::new(ParamType::Bytes))),
432 },
433 FieldDeclaration {
434 name: "s".to_string(),
435 ty: FieldType::Elementary(ParamType::FixedArray(Box::new(ParamType::String), 10)),
436 },
437 FieldDeclaration {
438 name: "m".to_string(),
439 ty: FieldType::Mapping(Box::new(
440 MappingType {
441 key_type: ParamType::Address,
442 value_type: FieldType::Elementary(ParamType::Uint(256))
443 }
444 )),
445 },
446 ],
447 }
448 );
449 }
450
451 #[test]
452 fn can_parse_struct_projections() {
453 assert_eq!(
454 SolStruct::parse("struct MyStruct{uint256 x; Some.Other.Inner _other;}").unwrap(),
455 SolStruct {
456 name: "MyStruct".to_string(),
457 fields: vec![
458 FieldDeclaration {
459 name: "x".to_string(),
460 ty: FieldType::Elementary(ParamType::Uint(256)),
461 },
462 FieldDeclaration {
463 name: "_other".to_string(),
464 ty: FieldType::Struct(StructFieldType::Type(StructType {
465 name: "Inner".to_string(),
466 projections: vec!["Some".to_string(), "Other".to_string()]
467 })),
468 },
469 ],
470 }
471 );
472 }
473
474 #[test]
475 fn can_parse_structs() {
476 [
477 "struct Demo {bytes x; address payable d;}",
478 "struct Demo2 {bytes[10] x; mapping(bool=> bool) d; int256 value;}",
479 "struct Struct { Other.MyStruct s; bool voted; address delegate; uint vote; }",
480 ]
481 .iter()
482 .for_each(|s| {
483 SolStruct::parse(s).unwrap();
484 });
485 }
486
487 #[test]
488 fn can_parse_mapping_type() {
489 assert_eq!(
490 parse_mapping("mapping(string=> string)").unwrap(),
491 MappingType {
492 key_type: ParamType::String,
493 value_type: FieldType::Elementary(ParamType::String)
494 }
495 );
496 }
497
498 #[test]
499 fn can_parse_nested_mappings() {
500 assert_eq!(
501 parse_mapping("mapping(string=> mapping(string=> string))").unwrap(),
502 MappingType {
503 key_type: ParamType::String,
504 value_type: FieldType::Mapping(Box::new(MappingType {
505 key_type: ParamType::String,
506 value_type: FieldType::Elementary(ParamType::String),
507 })),
508 }
509 );
510 }
511
512 #[test]
513 fn can_detect_illegal_mappings_key_type() {
514 [
515 "mapping(string[]=> mapping(string=> string))",
516 "mapping(bytes[10] => bool)",
517 "mapping(uint256[10] => bool)",
518 "mapping(Item=> bool)",
519 "mapping(Item[]=> mapping(address => bool))",
520 ]
521 .iter()
522 .for_each(|s| {
523 parse_mapping(s).unwrap_err();
524 });
525 }
526
527 #[test]
528 fn can_parse_mappings() {
529 [
530 "mapping(string=> mapping(string=> string))",
531 "mapping(string=> mapping(string=> mapping(string=> mapping(string=> string))))",
532 "mapping(bool=> bool)",
533 "mapping(bytes32 => bool)",
534 "mapping(bytes=> bool)",
535 "mapping(uint256=> mapping(address => bool))",
536 ]
537 .iter()
538 .for_each(|s| {
539 parse_mapping(s).unwrap();
540 });
541 }
542
543 #[test]
544 fn can_strip_field_ident() {
545 let mut s = "uint256 _myvar,
546 ";
547 let name = strip_field_identifier(&mut s).unwrap();
548 assert_eq!("_myvar", name);
549 assert_eq!("uint256", s);
550 }
551}