/ console / program / src / data / record / entry / parse.rs
parse.rs
  1  // Copyright (c) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the alphavm library.
  3  
  4  // Licensed under the Apache License, Version 2.0 (the "License");
  5  // you may not use this file except in compliance with the License.
  6  // You may obtain a copy of the License at:
  7  
  8  // http://www.apache.org/licenses/LICENSE-2.0
  9  
 10  // Unless required by applicable law or agreed to in writing, software
 11  // distributed under the License is distributed on an "AS IS" BASIS,
 12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  // See the License for the specific language governing permissions and
 14  // limitations under the License.
 15  
 16  use super::*;
 17  
 18  impl<N: Network> Parser for Entry<N, Plaintext<N>> {
 19      /// Parses a string into the entry.
 20      #[inline]
 21      fn parse(string: &str) -> ParserResult<Self> {
 22          /// A helper enum encoding the visibility.
 23          #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 24          enum Mode {
 25              Constant,
 26              Public,
 27              Private,
 28          }
 29  
 30          /// Parses a sanitized pair: `identifier: entry`, while tracking the depth of the data.
 31          fn parse_pair<N: Network>(string: &str, depth: usize) -> ParserResult<(Identifier<N>, Plaintext<N>, Mode)> {
 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 ":" from the string.
 39              let (string, _) = tag(":")(string)?;
 40              // Parse the whitespace from the string.
 41              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 42              // Parse the plaintext and visibility from the string.
 43              let (string, (plaintext, mode)) = alt((
 44                  // Parse a literal.
 45                  |input| parse_literal(input, depth),
 46                  // Parse a struct.
 47                  |input| parse_struct(input, depth),
 48                  // Parse an array.
 49                  |input| parse_array(input, depth),
 50              ))(string)?;
 51              // Parse the whitespace from the string.
 52              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 53              // Return the identifier, plaintext, and visibility.
 54              Ok((string, (identifier, plaintext, mode)))
 55          }
 56  
 57          /// Parses an entry as a literal: `literal.visibility`, while tracking the depth of the data.
 58          fn parse_literal<N: Network>(string: &str, depth: usize) -> ParserResult<(Plaintext<N>, Mode)> {
 59              // Ensure that the depth is within the maximum limit.
 60              if depth > N::MAX_DATA_DEPTH {
 61                  return map_res(take(0usize), |_| {
 62                      Err(error(format!("Found an entry future that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
 63                  })(string);
 64              }
 65              alt((
 66                  map(pair(Literal::parse, tag(".constant")), |(literal, _)| (Plaintext::from(literal), Mode::Constant)),
 67                  map(pair(Literal::parse, tag(".public")), |(literal, _)| (Plaintext::from(literal), Mode::Public)),
 68                  map(pair(Literal::parse, tag(".private")), |(literal, _)| (Plaintext::from(literal), Mode::Private)),
 69              ))(string)
 70          }
 71  
 72          /// Parses an entry as a struct: `{ identifier_0: plaintext_0.visibility, ..., identifier_n: plaintext_n.visibility }`, while tracking the depth of the data.
 73          /// Observe the `visibility` is the same for all members of the plaintext value.
 74          fn parse_struct<N: Network>(string: &str, depth: usize) -> ParserResult<(Plaintext<N>, Mode)> {
 75              // Ensure that the depth is within the maximum limit.
 76              if depth > N::MAX_DATA_DEPTH {
 77                  return map_res(take(0usize), |_| {
 78                      Err(error(format!("Found an entry that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
 79                  })(string);
 80              }
 81              // Parse the whitespace and comments from the string.
 82              let (string, _) = Sanitizer::parse(string)?;
 83              // Parse the "{" from the string.
 84              let (string, _) = tag("{")(string)?;
 85              // Parse the whitespace from the string.
 86              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 87              // Parse the members.
 88              let (string, (members, mode)) =
 89                  map_res(separated_list1(tag(","), |input| parse_pair(input, depth + 1)), |members: Vec<_>| {
 90                      // Ensure the members has no duplicate names.
 91                      if has_duplicates(members.iter().map(|(name, ..)| name)) {
 92                          return Err(error("Duplicate member in struct"));
 93                      }
 94                      // Ensure the members all have the same visibility.
 95                      let mode = members.iter().map(|(_, _, mode)| mode).dedup().collect::<Vec<_>>();
 96                      let mode = match mode.len() == 1 {
 97                          true => *mode[0],
 98                          false => return Err(error("Members of struct in entry have different visibilities")),
 99                      };
100                      // Ensure the number of structs is within the maximum limit.
101                      match members.len() <= N::MAX_STRUCT_ENTRIES {
102                          // Return the members and the visibility.
103                          true => Ok((members.into_iter().map(|(i, p, _)| (i, p)).collect::<Vec<_>>(), mode)),
104                          false => Err(error(format!("Found a struct that exceeds size ({})", members.len()))),
105                      }
106                  })(string)?;
107              // Parse the whitespace and comments from the string.
108              let (string, _) = Sanitizer::parse(string)?;
109              // Parse the '}' from the string.
110              let (string, _) = tag("}")(string)?;
111              // Output the plaintext and visibility.
112              Ok((string, (Plaintext::Struct(IndexMap::from_iter(members), Default::default()), mode)))
113          }
114  
115          /// Parses an entry as an array: `[plaintext_0.visibility, ..., plaintext_n.visibility]`, while tracking the depth of the data.
116          /// Observe the `visibility` is the same for all members of the plaintext value.
117          fn parse_array<N: Network>(string: &str, depth: usize) -> ParserResult<(Plaintext<N>, Mode)> {
118              // Ensure that the depth is within the maximum limit.
119              if depth > N::MAX_DATA_DEPTH {
120                  return map_res(take(0usize), |_| {
121                      Err(error(format!("Found an entry future that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
122                  })(string);
123              }
124              // Parse the whitespace and comments from the string.
125              let (string, _) = Sanitizer::parse(string)?;
126              // Parse the "[" from the string.
127              let (string, _) = tag("[")(string)?;
128              // Parse the whitespace from the string.
129              let (string, _) = Sanitizer::parse_whitespaces(string)?;
130              // Parse the members.
131              let (string, (elements, mode)) = map_res(
132                  separated_list1(
133                      pair(Sanitizer::parse_whitespaces, pair(tag(","), Sanitizer::parse_whitespaces)),
134                      alt((
135                          |input| parse_literal(input, depth + 1),
136                          |input| parse_struct(input, depth + 1),
137                          |input| parse_array(input, depth + 1),
138                      )),
139                  ),
140                  |members: Vec<(Plaintext<N>, Mode)>| {
141                      // Ensure the members all have the same visibility.
142                      let mode = members.iter().map(|(_, mode)| mode).dedup().collect::<Vec<_>>();
143                      let mode = match mode.len() == 1 {
144                          true => *mode[0],
145                          false => return Err(error("Members of an array have different visibilities")),
146                      };
147                      // Ensure the number of array elements is within the maximum limit.
148                      match members.len() <= N::MAX_ARRAY_ELEMENTS {
149                          // Return the members and the visibility.
150                          true => Ok((members.into_iter().map(|(p, _)| p).collect::<Vec<_>>(), mode)),
151                          false => Err(error(format!("Found an array that exceeds size ({})", members.len()))),
152                      }
153                  },
154              )(string)?;
155              // Parse the whitespace and comments from the string.
156              let (string, _) = Sanitizer::parse(string)?;
157              // Parse the ']' from the string.
158              let (string, _) = tag("]")(string)?;
159              // Output the plaintext and visibility.
160              Ok((string, (Plaintext::Array(elements, Default::default()), mode)))
161          }
162  
163          // Parse the whitespace from the string.
164          let (string, _) = Sanitizer::parse_whitespaces(string)?;
165          // Parse to determine the entry (order matters).
166          let (string, (plaintext, mode)) = alt((
167              // Parse a literal.
168              |input| parse_literal(input, 0),
169              // Parse a struct.
170              |input| parse_struct(input, 0),
171              // Parse an array.
172              |input| parse_array(input, 0),
173          ))(string)?;
174  
175          // Return the entry.
176          match mode {
177              Mode::Constant => Ok((string, Entry::Constant(plaintext))),
178              Mode::Public => Ok((string, Entry::Public(plaintext))),
179              Mode::Private => Ok((string, Entry::Private(plaintext))),
180          }
181      }
182  }
183  
184  impl<N: Network> FromStr for Entry<N, Plaintext<N>> {
185      type Err = Error;
186  
187      /// Returns the entry from a string literal.
188      fn from_str(string: &str) -> Result<Self> {
189          match Self::parse(string) {
190              Ok((remainder, object)) => {
191                  // Ensure the remainder is empty.
192                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
193                  // Return the object.
194                  Ok(object)
195              }
196              Err(error) => bail!("Failed to parse string. {error}"),
197          }
198      }
199  }
200  
201  impl<N: Network> Debug for Entry<N, Plaintext<N>> {
202      /// Prints the entry as a string.
203      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
204          Display::fmt(self, f)
205      }
206  }
207  
208  impl<N: Network> Display for Entry<N, Plaintext<N>> {
209      /// Prints the entry as a string.
210      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
211          self.fmt_internal(f, 0)
212      }
213  }
214  
215  impl<N: Network> Entry<N, Plaintext<N>> {
216      /// Prints the entry with the given indentation depth.
217      pub(in crate::data::record) fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
218          /// The number of spaces to indent.
219          const INDENT: usize = 2;
220  
221          let (plaintext, visibility) = match self {
222              Self::Constant(constant) => (constant, "constant"),
223              Self::Public(public) => (public, "public"),
224              Self::Private(private) => (private, "private"),
225          };
226  
227          match plaintext {
228              // Prints the literal, i.e. 10field.public
229              Plaintext::Literal(literal, ..) => {
230                  write!(f, "{:indent$}{literal}.{visibility}", "", indent = depth * INDENT)
231              }
232              // Prints the struct, i.e. { first: 10i64.private, second: 198u64.private }
233              Plaintext::Struct(struct_, ..) => {
234                  // Print the opening brace.
235                  write!(f, "{{")?;
236                  // Print the members.
237                  struct_.iter().enumerate().try_for_each(|(i, (name, plaintext))| {
238                      match plaintext {
239                          #[rustfmt::skip]
240                          Plaintext::Literal(literal, ..) => match i == struct_.len() - 1 {
241                              true => {
242                                  // Print the last member without a comma.
243                                  write!(f, "\n{:indent$}{name}: {literal}.{visibility}", "", indent = (depth + 1) * INDENT)?;
244                                  // Print the closing brace.
245                                  write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
246                              }
247                              // Print the member with a comma.
248                              false => write!(f, "\n{:indent$}{name}: {literal}.{visibility},", "", indent = (depth + 1) * INDENT),
249                          },
250                          Plaintext::Struct(..) | Plaintext::Array(..) => {
251                              // Print the member name.
252                              write!(f, "\n{:indent$}{name}: ", "", indent = (depth + 1) * INDENT)?;
253                              // Print the member.
254                              match self {
255                                  Self::Constant(..) => Self::Constant(plaintext.clone()).fmt_internal(f, depth + 1)?,
256                                  Self::Public(..) => Self::Public(plaintext.clone()).fmt_internal(f, depth + 1)?,
257                                  Self::Private(..) => Self::Private(plaintext.clone()).fmt_internal(f, depth + 1)?,
258                              }
259                              // Print the closing brace.
260                              match i == struct_.len() - 1 {
261                                  // If this inner struct is the last member of the outer struct, print the closing brace of the outer struct.
262                                  true => write!(f, "\n{:indent$}}}", "", indent = depth * INDENT),
263                                  // Otherwise, print a comma after the inner struct, because the outer struct has more members after this one.
264                                  false => write!(f, ","),
265                              }
266                          },
267                      }
268                  })
269              }
270              // Prints the array, i.e. [ 10u64.public, 198u64.private ]
271              Plaintext::Array(array, ..) => {
272                  // Print the opening bracket.
273                  write!(f, "[")?;
274                  // Print the members.
275                  array.iter().enumerate().try_for_each(|(i, plaintext)| {
276                      match plaintext {
277                          #[rustfmt::skip]
278                          Plaintext::Literal(literal, ..) => match i == array.len() - 1 {
279                              true => {
280                                  // Print the last member without a comma.
281                                  write!(f, "\n{:indent$}{literal}.{visibility}", "", indent = (depth + 1) * INDENT)?;
282                                  // Print the closing brace.
283                                  write!(f, "\n{:indent$}]", "", indent = depth * INDENT)
284                              }
285                              // Print the member with a comma.
286                              false => write!(f, "\n{:indent$}{literal}.{visibility},", "", indent = (depth + 1) * INDENT),
287                          },
288                          Plaintext::Struct(..) | Plaintext::Array(..) => {
289                              // Print a new line.
290                              write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT)?;
291                              // Print the member.
292                              match self {
293                                  Self::Constant(..) => Self::Constant(plaintext.clone()).fmt_internal(f, depth + 1)?,
294                                  Self::Public(..) => Self::Public(plaintext.clone()).fmt_internal(f, depth + 1)?,
295                                  Self::Private(..) => Self::Private(plaintext.clone()).fmt_internal(f, depth + 1)?,
296                              }
297                              // Print the closing brace.
298                              match i == array.len() - 1 {
299                                  // If this inner struct is the last member of the outer struct, print the closing bracket of the outer vector.
300                                  true => write!(f, "\n{:indent$}]", "", indent = depth * INDENT),
301                                  // Otherwise, print a comma after the inner struct, because the outer vector has more members after this one.
302                                  false => write!(f, ","),
303                              }
304                          },
305                      }
306                  })
307              }
308          }
309      }
310  }
311  
312  #[cfg(test)]
313  mod tests {
314      use super::*;
315      use alphavm_console_network::MainnetV0;
316  
317      type CurrentNetwork = MainnetV0;
318  
319      #[test]
320      fn test_parse() -> Result<()> {
321          // Sanity check.
322          let expected = r"{
323    foo: 5u8.private
324  }";
325          let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse("{ foo: 5u8.private }")?;
326          assert_eq!(expected, candidate.to_string());
327          assert_eq!("", remainder);
328  
329          let expected = r"{
330    foo: 5u8.public,
331    bar: {
332      baz: 10field.public,
333      qux: {
334        quux: {
335          corge: {
336            grault: {
337              garply: {
338                waldo: {
339                  fred: {
340                    plugh: {
341                      xyzzy: {
342                        thud: true.public
343                      }
344                    }
345                  }
346                }
347              }
348            }
349          }
350        }
351      }
352    }
353  }";
354          let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
355              "{ foo: 5u8.public, bar: { baz: 10field.public, qux: {quux:{corge :{grault:  {garply:{waldo:{fred:{plugh:{xyzzy: { thud: true.public}} }}}  }}}}}}",
356          )?;
357          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
358          assert_eq!(expected, candidate.to_string());
359          assert_eq!("", remainder);
360  
361          // Test an array of literals.
362          let expected = r"[
363    5u8.private,
364    10u8.private,
365    15u8.private
366  ]";
367          let (remainder, candidate) =
368              Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse("[ 5u8.private, 10u8.private, 15u8.private ]")?;
369          assert_eq!(expected, candidate.to_string());
370          assert_eq!("", remainder);
371  
372          // Test an array of structs.
373          let expected = r"[
374    {
375      foo: 5u8.public
376    },
377    {
378      bar: 10u8.public
379    },
380    {
381      baz: 15u8.public
382    }
383  ]";
384          let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
385              "[ { foo: 5u8.public }, { bar: 10u8.public }, { baz: 15u8.public } ]",
386          )?;
387          assert_eq!(expected, candidate.to_string());
388          assert_eq!("", remainder);
389  
390          // Test a struct with arrays.
391          let expected = r"{
392    foo: [
393      5u8.public,
394      10u8.public,
395      15u8.public
396    ],
397    bar: [
398      {
399        foo: 5u8.public
400      },
401      {
402        bar: 10u8.public
403      },
404      {
405        baz: 15u8.public
406      }
407    ]
408  }";
409          let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
410              "{ foo: [ 5u8.public, 10u8.public, 15u8.public ], bar: [ { foo: 5u8.public }, { bar: 10u8.public }, { baz: 15u8.public } ] }",
411          )?;
412          assert_eq!(expected, candidate.to_string());
413          assert_eq!("", remainder);
414  
415          Ok(())
416      }
417  
418      // A helper function to get the depth of the plaintext.
419      fn get_depth(plaintext: &Plaintext<CurrentNetwork>) -> usize {
420          match plaintext {
421              Plaintext::Literal(_, _) => 0,
422              Plaintext::Struct(members, _) => members.values().map(get_depth).max().unwrap_or(0) + 1,
423              Plaintext::Array(elements, _) => elements.iter().map(get_depth).max().unwrap_or(0) + 1,
424          }
425      }
426  
427      #[test]
428      fn test_deeply_nested_entry() {
429          // Creates a string representation of a nested array Entry with the given depth and root.
430          fn create_nested_array(depth: usize, root: impl Display) -> String {
431              // Define the prefix and suffix based on the depth.
432              let prefix = if depth == 0 { "".to_string() } else { "[".repeat(depth) };
433              let suffix = if depth == 0 { "".to_string() } else { "]".repeat(depth) };
434              // Format the string with the prefix, root, and suffix.
435              format!("{prefix}{root}{suffix}")
436          }
437  
438          // Creates a string representation of a nested struct Entry with the given depth and root.
439          fn create_nested_struct(depth: usize, root: impl Display) -> String {
440              // Define the prefix and suffix based on the depth.
441              let prefix = if depth == 0 { "".to_string() } else { "{inner:".repeat(depth) };
442              let suffix = if depth == 0 { "".to_string() } else { "}".repeat(depth) };
443              // Format the string with the prefix, root, and suffix.
444              format!("{prefix}{root}{suffix}")
445          }
446  
447          // Creates a string representation of a nested Entry with alternating structs and arrays with the given depth and root.
448          fn create_alternated_nested(depth: usize, root: impl Display) -> String {
449              let prefix = (0..depth).map(|i| if i % 2 == 0 { "[" } else { "{inner:" }).collect::<String>();
450              let suffix = (0..depth).map(|i| if i % 2 == 0 { "]" } else { "}" }).rev().collect::<String>();
451              format!("{prefix}{root}{suffix}")
452          }
453  
454          // A helper function to run the test.
455          fn run_test(expected_depth: usize, input: String, expected_error: bool) {
456              println!("Testing input: {input} with expected error: {expected_error}");
457              // Parse the input string.
458              let result = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(&input);
459              // Check if the result is an error.
460              match expected_error {
461                  true => {
462                      assert!(result.is_err());
463                      return;
464                  }
465                  false => assert!(result.is_ok()),
466              };
467              // Unwrap the result.
468              let (remainder, candidate) = result.unwrap();
469              // Check if the remainder is empty.
470              assert!(remainder.is_empty());
471              // Check if the candidate is equal to the input, with whitespace removed.
472              assert_eq!(input, candidate.to_string().replace("\n", "").replace(" ", ""));
473              // Check if the candidate is equal to the expected depth.
474              match candidate {
475                  Entry::Constant(plaintext) => {
476                      assert_eq!(get_depth(&plaintext), expected_depth);
477                  }
478                  Entry::Public(plaintext) => {
479                      assert_eq!(get_depth(&plaintext), expected_depth);
480                  }
481                  Entry::Private(plaintext) => {
482                      assert_eq!(get_depth(&plaintext), expected_depth);
483                  }
484              }
485          }
486  
487          // Initialize a sequence of depths to check.
488          let mut depths = (0usize..100).collect_vec();
489          depths.extend((100..1000).step_by(100));
490          depths.extend((1000..10000).step_by(1000));
491          depths.extend((10000..100000).step_by(10000));
492  
493          // Test deeply nested arrays with different literal types.
494          for i in depths.iter().copied() {
495              run_test(i, create_nested_array(i, "false.constant"), i > CurrentNetwork::MAX_DATA_DEPTH);
496              run_test(i, create_nested_array(i, "1u8.public"), i > CurrentNetwork::MAX_DATA_DEPTH);
497              run_test(i, create_nested_array(i, "0u128.private"), i > CurrentNetwork::MAX_DATA_DEPTH);
498              run_test(i, create_nested_array(i, "10field.constant"), i > CurrentNetwork::MAX_DATA_DEPTH);
499          }
500  
501          // Test deeply nested structs with different literal types.
502          for i in depths.iter().copied() {
503              run_test(i, create_nested_struct(i, "false.public"), i > CurrentNetwork::MAX_DATA_DEPTH);
504              run_test(i, create_nested_struct(i, "1u8.private"), i > CurrentNetwork::MAX_DATA_DEPTH);
505              run_test(i, create_nested_struct(i, "0u128.constant"), i > CurrentNetwork::MAX_DATA_DEPTH);
506              run_test(i, create_nested_struct(i, "10field.public"), i > CurrentNetwork::MAX_DATA_DEPTH);
507          }
508  
509          // Test alternating nested arrays and structs.
510          for i in depths.iter().copied() {
511              run_test(i, create_alternated_nested(i, "false.private"), i > CurrentNetwork::MAX_DATA_DEPTH);
512              run_test(i, create_alternated_nested(i, "1u8.constant"), i > CurrentNetwork::MAX_DATA_DEPTH);
513              run_test(i, create_alternated_nested(i, "0u128.public"), i > CurrentNetwork::MAX_DATA_DEPTH);
514              run_test(i, create_alternated_nested(i, "10field.private"), i > CurrentNetwork::MAX_DATA_DEPTH);
515          }
516      }
517  }