ethabi_next/param_type/
reader.rs

1// Copyright 2015-2020 Parity Technologies
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use crate::{Error, ParamType};
10
11/// Used to convert param type represented as a string to rust structure.
12pub struct Reader;
13
14impl Reader {
15	/// Converts string to param type.
16	pub fn read(name: &str) -> Result<ParamType, Error> {
17		match name.chars().last() {
18			// check if it is a struct
19			Some(')') => {
20				if !name.starts_with('(') {
21					return Err(Error::InvalidName(name.to_owned()));
22				};
23
24				let mut subtypes = Vec::new();
25				let mut subtuples = Vec::new();
26				let mut nested = 0isize;
27				let mut top_level_paren_open = 0usize;
28				let mut last_item = 1;
29				let mut chars = name.chars().enumerate();
30
31				// Iterate over name and build the nested tuple structure
32				while let Some((mut pos, c)) = chars.next() {
33					match c {
34						'(' => {
35							top_level_paren_open = pos;
36							nested += 1;
37							// If an '(' is encountered within the tuple
38							// insert an empty subtuples vector to be filled
39							if nested > 1 {
40								subtuples.push(vec![]);
41								last_item = pos + 1;
42							}
43						}
44						')' => {
45							nested -= 1;
46
47							// End parsing and return an error if parentheses aren't symmetrical
48							if nested < 0 {
49								return Err(Error::InvalidName(name.to_owned()));
50							}
51							// If there have not been any characters since the last item
52							// increment position without inserting any subtypes
53							else if name[last_item..pos].is_empty() {
54								last_item = pos + 1;
55							}
56							// If the item is in the top level of the tuple insert it into subtypes
57							else if nested == 0 {
58								// check for trailing brackets that indicate array of tuples
59								let sub = &name[last_item..pos];
60								let subtype = Reader::read(sub)?;
61								subtypes.push(subtype);
62								last_item = pos + 1;
63							}
64							// If the item is in a sublevel of the tuple
65							else if nested > 0 {
66								// this makes sure trailing brackets are included for the next step
67								loop {
68									match chars.clone().next() {
69										Some((_, ',')) | Some((_, ')')) | None => break,
70										_ => {
71											// consume the char and shift position
72											chars.next();
73											pos += 1;
74										}
75									}
76								}
77
78								// parse the nested tuple
79								let inner_tuple = &name[top_level_paren_open..=pos];
80								let subtype = Reader::read(inner_tuple)?;
81
82								if nested > 1 {
83									subtuples[(nested - 2) as usize].push(subtype);
84									subtypes.push(ParamType::Tuple(std::mem::replace(
85										&mut subtuples[(nested - 2) as usize],
86										Vec::new(),
87									)));
88								} else {
89									subtypes.push(subtype);
90								}
91								last_item = pos + 1;
92							}
93						}
94						',' => {
95							// If there have not been any characters since the last item
96							// increment position without inserting any subtypes
97							if name[last_item..pos].is_empty() {
98								last_item = pos + 1
99							}
100							// If the item is in the top level of the tuple insert it into subtypes
101							else if nested == 1 {
102								let sub = &name[last_item..pos];
103								let subtype = Reader::read(sub)?;
104								subtypes.push(subtype);
105								last_item = pos + 1;
106							}
107							// If the item is in a sublevel of the tuple
108							// insert it into the subtuple vector for the current depth level
109							else if nested > 1 {
110								let sub = &name[last_item..pos];
111								let subtype = Reader::read(sub)?;
112								subtuples[(nested - 2) as usize].push(subtype);
113								last_item = pos + 1;
114							}
115						}
116						_ => (),
117					}
118				}
119				return Ok(ParamType::Tuple(subtypes));
120			}
121			// check if it is a fixed or dynamic array.
122			Some(']') => {
123				// take number part
124				let num: String =
125					name.chars().rev().skip(1).take_while(|c| *c != '[').collect::<String>().chars().rev().collect();
126
127				let count = name.chars().count();
128				return if num.is_empty() {
129					// we already know it's a dynamic array!
130					let subtype = Reader::read(&name[..count - 2])?;
131					Ok(ParamType::Array(Box::new(subtype)))
132				} else {
133					// it's a fixed array.
134					let len = usize::from_str_radix(&num, 10)?;
135					let subtype = Reader::read(&name[..count - num.len() - 2])?;
136					Ok(ParamType::FixedArray(Box::new(subtype), len))
137				};
138			}
139			_ => (),
140		}
141
142		let result = match name {
143			"address" => ParamType::Address,
144			"bytes" => ParamType::Bytes,
145			"bool" => ParamType::Bool,
146			"string" => ParamType::String,
147			"int" => ParamType::Int(256),
148			"tuple" => ParamType::Tuple(vec![]),
149			"uint" => ParamType::Uint(256),
150			s if s.starts_with("int") => {
151				let len = usize::from_str_radix(&s[3..], 10)?;
152				ParamType::Int(len)
153			}
154			s if s.starts_with("uint") => {
155				let len = usize::from_str_radix(&s[4..], 10)?;
156				ParamType::Uint(len)
157			}
158			s if s.starts_with("bytes") => {
159				let len = usize::from_str_radix(&s[5..], 10)?;
160				ParamType::FixedBytes(len)
161			}
162			_ => {
163				return Err(Error::InvalidName(name.to_owned()));
164			}
165		};
166
167		Ok(result)
168	}
169}
170
171#[cfg(test)]
172mod tests {
173	use super::Reader;
174	use crate::ParamType;
175
176	#[test]
177	fn test_read_param() {
178		assert_eq!(Reader::read("address").unwrap(), ParamType::Address);
179		assert_eq!(Reader::read("bytes").unwrap(), ParamType::Bytes);
180		assert_eq!(Reader::read("bytes32").unwrap(), ParamType::FixedBytes(32));
181		assert_eq!(Reader::read("bool").unwrap(), ParamType::Bool);
182		assert_eq!(Reader::read("string").unwrap(), ParamType::String);
183		assert_eq!(Reader::read("int").unwrap(), ParamType::Int(256));
184		assert_eq!(Reader::read("uint").unwrap(), ParamType::Uint(256));
185		assert_eq!(Reader::read("int32").unwrap(), ParamType::Int(32));
186		assert_eq!(Reader::read("uint32").unwrap(), ParamType::Uint(32));
187	}
188
189	#[test]
190	fn test_read_array_param() {
191		assert_eq!(Reader::read("address[]").unwrap(), ParamType::Array(Box::new(ParamType::Address)));
192		assert_eq!(Reader::read("uint[]").unwrap(), ParamType::Array(Box::new(ParamType::Uint(256))));
193		assert_eq!(Reader::read("bytes[]").unwrap(), ParamType::Array(Box::new(ParamType::Bytes)));
194		assert_eq!(
195			Reader::read("bool[][]").unwrap(),
196			ParamType::Array(Box::new(ParamType::Array(Box::new(ParamType::Bool))))
197		);
198	}
199
200	#[test]
201	fn test_read_fixed_array_param() {
202		assert_eq!(Reader::read("address[2]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Address), 2));
203		assert_eq!(Reader::read("bool[17]").unwrap(), ParamType::FixedArray(Box::new(ParamType::Bool), 17));
204		assert_eq!(
205			Reader::read("bytes[45][3]").unwrap(),
206			ParamType::FixedArray(Box::new(ParamType::FixedArray(Box::new(ParamType::Bytes), 45)), 3)
207		);
208	}
209
210	#[test]
211	fn test_read_mixed_arrays() {
212		assert_eq!(
213			Reader::read("bool[][3]").unwrap(),
214			ParamType::FixedArray(Box::new(ParamType::Array(Box::new(ParamType::Bool))), 3)
215		);
216		assert_eq!(
217			Reader::read("bool[3][]").unwrap(),
218			ParamType::Array(Box::new(ParamType::FixedArray(Box::new(ParamType::Bool), 3)))
219		);
220	}
221
222	#[test]
223	fn test_read_struct_param() {
224		assert_eq!(
225			Reader::read("(address,bool)").unwrap(),
226			ParamType::Tuple(vec![ParamType::Address, ParamType::Bool])
227		);
228		assert_eq!(
229			Reader::read("(bool[3],uint256)").unwrap(),
230			ParamType::Tuple(vec![ParamType::FixedArray(Box::new(ParamType::Bool), 3), ParamType::Uint(256)])
231		);
232	}
233
234	#[test]
235	fn test_read_nested_struct_param() {
236		assert_eq!(
237			Reader::read("(address,bool,(bool,uint256))").unwrap(),
238			ParamType::Tuple(vec![
239				ParamType::Address,
240				ParamType::Bool,
241				ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
242			])
243		);
244	}
245
246	#[test]
247	fn test_read_complex_nested_struct_param() {
248		assert_eq!(
249			Reader::read("(address,bool,(bool,uint256,(bool,uint256)),(bool,uint256))").unwrap(),
250			ParamType::Tuple(vec![
251				ParamType::Address,
252				ParamType::Bool,
253				ParamType::Tuple(vec![
254					ParamType::Bool,
255					ParamType::Uint(256),
256					ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
257				]),
258				ParamType::Tuple(vec![ParamType::Bool, ParamType::Uint(256)])
259			])
260		);
261	}
262
263	#[test]
264	fn test_read_nested_tuple_array_param() {
265		assert_eq!(
266			Reader::read("(uint256,bytes32)[]").unwrap(),
267			ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Uint(256), ParamType::FixedBytes(32)])))
268		)
269	}
270
271	#[test]
272	fn test_read_inner_tuple_array_param() {
273		use crate::param_type::Writer;
274		let abi = "((uint256,bytes32)[],address)";
275		let read = Reader::read(abi).unwrap();
276
277		let param = ParamType::Tuple(vec![
278			ParamType::Array(Box::new(ParamType::Tuple(vec![ParamType::Uint(256), ParamType::FixedBytes(32)]))),
279			ParamType::Address,
280		]);
281
282		assert_eq!(read, param);
283
284		assert_eq!(abi, Writer::write(&param));
285	}
286}