parse.rs
  1  // Copyright (c) 2025-2026 ACDC Network
  2  // This file is part of the alphavm library.
  3  //
  4  // Alpha Chain | Delta Chain Protocol
  5  // International Monetary Graphite.
  6  //
  7  // Derived from Aleo (https://aleo.org) and ProvableHQ (https://provable.com).
  8  // They built world-class ZK infrastructure. We installed the EASY button.
  9  // Their cryptography: elegant. Our modifications: bureaucracy-compatible.
 10  // Original brilliance: theirs. Robert's Rules: ours. Bugs: definitely ours.
 11  //
 12  // Original Aleo/ProvableHQ code subject to Apache 2.0 https://www.apache.org/licenses/LICENSE-2.0
 13  // All modifications and new work: CC0 1.0 Universal Public Domain Dedication.
 14  // No rights reserved. No permission required. No warranty. No refunds.
 15  //
 16  // https://creativecommons.org/publicdomain/zero/1.0/
 17  // SPDX-License-Identifier: CC0-1.0
 18  
 19  use super::*;
 20  
 21  impl<N: Network> Parser for StructType<N> {
 22      /// Parses a struct as:
 23      /// ```text
 24      ///   struct message:
 25      ///       owner as address;
 26      ///       amount as u64;
 27      /// ```
 28      #[inline]
 29      fn parse(string: &str) -> ParserResult<'_, Self> {
 30          /// Parses a string into a tuple.
 31          fn parse_tuple<N: Network>(string: &str) -> ParserResult<'_, (Identifier<N>, PlaintextType<N>)> {
 32              // Parse the whitespace and comments from the string.
 33              let (string, _) = Sanitizer::parse(string)?;
 34              // Parse the identifier from the string.
 35              let (string, identifier) = Identifier::parse(string)?;
 36              // Parse the whitespace from the string.
 37              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 38              // Parse the "as" from the string.
 39              let (string, _) = tag("as")(string)?;
 40              // Parse the whitespace from the string.
 41              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 42              // Parse the plaintext type from the string.
 43              let (string, plaintext_type) = PlaintextType::parse(string)?;
 44              // Parse the whitespace from the string.
 45              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 46              // Parse the semicolon ';' keyword from the string.
 47              let (string, _) = tag(";")(string)?;
 48              // Return the identifier and plaintext type.
 49              Ok((string, (identifier, plaintext_type)))
 50          }
 51  
 52          // Parse the whitespace and comments from the string.
 53          let (string, _) = Sanitizer::parse(string)?;
 54          // Parse the type name from the string.
 55          let (string, _) = tag(Self::type_name())(string)?;
 56          // Parse the whitespace from the string.
 57          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 58          // Parse the struct name from the string.
 59          let (string, name) = Identifier::parse(string)?;
 60          // Parse the whitespace from the string.
 61          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 62          // Parse the colon ':' keyword from the string.
 63          let (string, _) = tag(":")(string)?;
 64          // Parse the members from the string.
 65          let (string, members) = map_res(many1(parse_tuple), |members| {
 66              // Ensure the members has no duplicate names.
 67              if has_duplicates(members.iter().map(|(identifier, _)| identifier)) {
 68                  return Err(error(format!("Duplicate identifier found in struct '{name}'")));
 69              }
 70              // Ensure the number of members is within the maximum limit.
 71              if members.len() > N::MAX_STRUCT_ENTRIES {
 72                  return Err(error("Failed to parse struct: too many members"));
 73              }
 74              Ok(members)
 75          })(string)?;
 76          // Return the struct.
 77          Ok((string, Self { name, members: IndexMap::from_iter(members) }))
 78      }
 79  }
 80  
 81  impl<N: Network> FromStr for StructType<N> {
 82      type Err = Error;
 83  
 84      /// Returns a struct from a string literal.
 85      fn from_str(string: &str) -> Result<Self> {
 86          match Self::parse(string) {
 87              Ok((remainder, object)) => {
 88                  // Ensure the remainder is empty.
 89                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
 90                  // Return the object.
 91                  Ok(object)
 92              }
 93              Err(error) => bail!("Failed to parse string. {error}"),
 94          }
 95      }
 96  }
 97  
 98  impl<N: Network> Debug for StructType<N> {
 99      /// Prints the struct type as a string.
100      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
101          Display::fmt(self, f)
102      }
103  }
104  
105  #[allow(clippy::format_push_string)]
106  impl<N: Network> Display for StructType<N> {
107      /// Prints the struct type as a string.
108      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
109          let mut output = format!("{} {}:\n", Self::type_name(), self.name);
110          for (identifier, plaintext_type) in &self.members {
111              output += &format!("    {identifier} as {plaintext_type};\n");
112          }
113          output.pop(); // trailing newline
114          write!(f, "{output}")
115      }
116  }
117  
118  #[cfg(test)]
119  mod tests {
120      use super::*;
121      use alphavm_console_network::MainnetV0;
122  
123      type CurrentNetwork = MainnetV0;
124  
125      #[test]
126      fn test_parse() -> Result<()> {
127          let expected = StructType::<CurrentNetwork> {
128              name: Identifier::from_str("message")?,
129              members: IndexMap::from_iter(vec![
130                  (Identifier::from_str("sender")?, PlaintextType::from_str("address")?),
131                  (Identifier::from_str("amount")?, PlaintextType::from_str("u64")?),
132              ]),
133          };
134  
135          let (remainder, candidate) = StructType::<CurrentNetwork>::parse(
136              r"
137  struct message:
138      sender as address;
139      amount as u64;
140  ",
141          )?;
142          assert_eq!("\n", remainder);
143          assert_eq!(expected, candidate);
144          Ok(())
145      }
146  
147      #[test]
148      fn test_parse_fails() {
149          // Must be non-empty.
150          assert!(StructType::<CurrentNetwork>::parse("").is_err());
151          assert!(StructType::<CurrentNetwork>::parse("struct message:").is_err());
152  
153          // Invalid characters.
154          assert!(StructType::<CurrentNetwork>::parse("{}").is_err());
155          assert!(StructType::<CurrentNetwork>::parse("_").is_err());
156          assert!(StructType::<CurrentNetwork>::parse("__").is_err());
157          assert!(StructType::<CurrentNetwork>::parse("___").is_err());
158          assert!(StructType::<CurrentNetwork>::parse("-").is_err());
159          assert!(StructType::<CurrentNetwork>::parse("--").is_err());
160          assert!(StructType::<CurrentNetwork>::parse("---").is_err());
161          assert!(StructType::<CurrentNetwork>::parse("*").is_err());
162          assert!(StructType::<CurrentNetwork>::parse("**").is_err());
163          assert!(StructType::<CurrentNetwork>::parse("***").is_err());
164  
165          // Must not start with a number.
166          assert!(StructType::<CurrentNetwork>::parse("1").is_err());
167          assert!(StructType::<CurrentNetwork>::parse("2").is_err());
168          assert!(StructType::<CurrentNetwork>::parse("3").is_err());
169          assert!(StructType::<CurrentNetwork>::parse("1foo").is_err());
170          assert!(StructType::<CurrentNetwork>::parse("12").is_err());
171          assert!(StructType::<CurrentNetwork>::parse("111").is_err());
172  
173          // Must fit within the data capacity of a base field element.
174          let struct_ =
175              StructType::<CurrentNetwork>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy");
176          assert!(struct_.is_err());
177      }
178  
179      #[test]
180      fn test_display() {
181          let expected = "struct message:\n    first as field;\n    second as field;";
182          let message = StructType::<CurrentNetwork>::parse(expected).unwrap().1;
183          assert_eq!(expected, format!("{message}"));
184      }
185  
186      #[test]
187      fn test_display_fails() {
188          // Duplicate identifier.
189          let candidate =
190              StructType::<CurrentNetwork>::parse("struct message:\n    first as field;\n    first as field;");
191          assert!(candidate.is_err());
192          // Visibility in plaintext type.
193          let candidate = StructType::<CurrentNetwork>::parse(
194              "struct message:\n    first as field.public;\n    first as field.private;",
195          );
196          assert!(candidate.is_err());
197      }
198  
199      #[test]
200      fn test_max_members() {
201          let mut string = "struct message:\n".to_string();
202          for i in 0..CurrentNetwork::MAX_STRUCT_ENTRIES {
203              string += &format!("    member_{i} as field;\n");
204          }
205          assert!(StructType::<CurrentNetwork>::parse(&string).is_ok());
206      }
207  
208      #[test]
209      fn test_too_many_members() {
210          let mut string = "struct message:\n".to_string();
211          for i in 0..=CurrentNetwork::MAX_STRUCT_ENTRIES {
212              string += &format!("    member_{i} as field;\n");
213          }
214          assert!(StructType::<CurrentNetwork>::parse(&string).is_err());
215      }
216  }