/ console / program / src / data / record / parse_plaintext.rs
parse_plaintext.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 Record<N, Plaintext<N>> {
 19      /// Parses a string as a record: `{ owner: address, identifier_0: entry_0, ..., identifier_n: entry_n, _nonce: field, _version: u8 }`.
 20      #[inline]
 21      fn parse(string: &str) -> ParserResult<Self> {
 22          /// Parses a sanitized pair: `identifier: entry`.
 23          #[allow(clippy::type_complexity)]
 24          fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<N>, Entry<N, Plaintext<N>>)> {
 25              // Parse the whitespace and comments from the string.
 26              let (string, _) = Sanitizer::parse(string)?;
 27              // Parse the identifier from the string.
 28              let (string, identifier) = Identifier::parse(string)?;
 29              // Parse the whitespace from the string.
 30              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 31              // Parse the ":" from the string.
 32              let (string, _) = tag(":")(string)?;
 33              // Parse the whitespace and comments from the string.
 34              let (string, _) = Sanitizer::parse(string)?;
 35              // Parse the entry from the string.
 36              let (string, entry) = Entry::parse(string)?;
 37              // Parse the whitespace from the string.
 38              let (string, _) = Sanitizer::parse_whitespaces(string)?;
 39              // Return the identifier and entry.
 40              Ok((string, (identifier, entry)))
 41          }
 42  
 43          // Parse the whitespace and comments from the string.
 44          let (string, _) = Sanitizer::parse(string)?;
 45          // Parse the "{" from the string.
 46          let (string, _) = tag("{")(string)?;
 47  
 48          // Parse the whitespace and comments from the string.
 49          let (string, _) = Sanitizer::parse(string)?;
 50          // Parse the "owner" tag from the string.
 51          let (string, _) = tag("owner")(string)?;
 52          // Parse the whitespace from the string.
 53          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 54          // Parse the ":" from the string.
 55          let (string, _) = tag(":")(string)?;
 56          // Parse the whitespace and comments from the string.
 57          let (string, _) = Sanitizer::parse(string)?;
 58          // Parse the owner from the string.
 59          let (string, owner) = alt((
 60              map(pair(Address::parse, tag(".public")), |(owner, _)| Owner::Public(owner)),
 61              map(pair(Address::parse, tag(".private")), |(owner, _)| {
 62                  Owner::Private(Plaintext::from(Literal::Address(owner)))
 63              }),
 64          ))(string)?;
 65          // Parse the "," from the string.
 66          let (string, _) = tag(",")(string)?;
 67  
 68          // Parse the entries.
 69          let (string, entries) = map_res(separated_list0(tag(","), parse_pair), |entries: Vec<_>| {
 70              // Prepare the reserved entry names.
 71              let reserved = [Identifier::from_str("owner").map_err(|e| error(e.to_string()))?];
 72              // Ensure the entries has no duplicate names.
 73              if has_duplicates(entries.iter().map(|(identifier, _)| identifier).chain(reserved.iter())) {
 74                  return Err(error("Duplicate entry type found in record"));
 75              }
 76              // Ensure the number of entries is within the maximum limit.
 77              match entries.len() <= N::MAX_DATA_ENTRIES {
 78                  true => Ok(entries),
 79                  false => Err(error(format!("Found a record that exceeds size ({})", entries.len()))),
 80              }
 81          })(string)?;
 82  
 83          // If there are entries, then parse the "," from the string.
 84          let string = match !entries.is_empty() {
 85              // Parse the "," from the string.
 86              true => tag(",")(string)?.0,
 87              false => string,
 88          };
 89  
 90          // Parse the whitespace and comments from the string.
 91          let (string, _) = Sanitizer::parse(string)?;
 92          // Parse the "_nonce" tag from the string.
 93          let (string, _) = tag("_nonce")(string)?;
 94          // Parse the ":" from the string.
 95          let (string, _) = tag(":")(string)?;
 96          // Parse the whitespace and comments from the string.
 97          let (string, _) = Sanitizer::parse(string)?;
 98          // Parse the nonce from the string.
 99          let (string, (nonce, _)) = pair(Group::parse, tag(".public"))(string)?;
100  
101          // There may be an optional "_version" tag. Parse the "," from the string if it exists.
102          let string = match opt(tag(","))(string)? {
103              // If there is a version, then parse the "," from the string.
104              (string, Some(_)) => string,
105              // If there is no version, then keep the string as is.
106              (string, None) => string,
107          };
108  
109          // Parse the whitespace and comments from the string.
110          let (string, _) = Sanitizer::parse(string)?;
111          // Parse the optional "_version" tag from the string.
112          let (string, version) = match opt(tag("_version"))(string)? {
113              // If there is no version, then set the version to zero.
114              (string, None) => (string, U8::zero()),
115              // If there is a version, then parse the version from the string.
116              (string, Some(_)) => {
117                  // Parse the whitespace and comments from the string.
118                  let (string, _) = Sanitizer::parse(string)?;
119                  // Parse the ":" from the string.
120                  let (string, _) = tag(":")(string)?;
121                  // Parse the whitespace and comments from the string.
122                  let (string, _) = Sanitizer::parse(string)?;
123                  // Parse the version from the string.
124                  terminated(U8::parse, tag(".public"))(string)?
125              }
126          };
127  
128          // Parse the whitespace and comments from the string.
129          let (string, _) = Sanitizer::parse(string)?;
130          // Parse the '}' from the string.
131          let (string, _) = tag("}")(string)?;
132          // Output the record.
133          Ok((string, Record { owner, data: IndexMap::from_iter(entries), nonce, version }))
134      }
135  }
136  
137  impl<N: Network> FromStr for Record<N, Plaintext<N>> {
138      type Err = Error;
139  
140      /// Returns a record from a string literal.
141      fn from_str(string: &str) -> Result<Self> {
142          match Self::parse(string) {
143              Ok((remainder, object)) => {
144                  // Ensure the remainder is empty.
145                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
146                  // Return the object.
147                  Ok(object)
148              }
149              Err(error) => bail!("Failed to parse string. {error}"),
150          }
151      }
152  }
153  
154  impl<N: Network> Debug for Record<N, Plaintext<N>> {
155      /// Prints the record as a string.
156      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
157          Display::fmt(self, f)
158      }
159  }
160  
161  impl<N: Network> Display for Record<N, Plaintext<N>> {
162      /// Prints the record as a string.
163      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
164          self.fmt_internal(f, 0)
165      }
166  }
167  
168  impl<N: Network> Record<N, Plaintext<N>> {
169      /// Prints the record with the given indentation depth.
170      fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
171          /// The number of spaces to indent.
172          const INDENT: usize = 2;
173  
174          // Print the opening brace.
175          write!(f, "{{")?;
176          // Print the owner with a comma.
177          write!(f, "\n{:indent$}owner: {},", "", self.owner, indent = (depth + 1) * INDENT)?;
178          // Print the data with a comma.
179          for (identifier, entry) in self.data.iter() {
180              // Print the identifier.
181              write!(f, "\n{:indent$}{identifier}: ", "", indent = (depth + 1) * INDENT)?;
182              // Print the entry.
183              match entry {
184                  // If the entry is a literal, print the entry without indentation.
185                  Entry::Constant(Plaintext::Literal(..))
186                  | Entry::Public(Plaintext::Literal(..))
187                  | Entry::Private(Plaintext::Literal(..)) => write!(f, "{entry}")?,
188                  // If the entry is a struct or an array, print the entry with indentation.
189                  Entry::Constant(Plaintext::Struct(..))
190                  | Entry::Public(Plaintext::Struct(..))
191                  | Entry::Private(Plaintext::Struct(..))
192                  | Entry::Constant(Plaintext::Array(..))
193                  | Entry::Public(Plaintext::Array(..))
194                  | Entry::Private(Plaintext::Array(..)) => entry.fmt_internal(f, depth + 1)?,
195              }
196              // Print the comma.
197              write!(f, ",")?;
198          }
199          // Print the nonce with a comma.
200          write!(f, "\n{:indent$}_nonce: {}.public,", "", self.nonce, indent = (depth + 1) * INDENT)?;
201          // Print the version without a comma.
202          write!(f, "\n{:indent$}_version: {}.public", "", self.version, indent = (depth + 1) * INDENT)?;
203          // Print the closing brace.
204          write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
205      }
206  }
207  
208  #[cfg(test)]
209  mod tests {
210      use super::*;
211      use alphavm_console_network::MainnetV0;
212  
213      type CurrentNetwork = MainnetV0;
214  
215      #[test]
216      fn test_parse_without_version() -> Result<()> {
217          // Sanity check.
218          let expected = r"{
219    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
220    _nonce: 0group.public,
221    _version: 0u8.public
222  }";
223          let given =
224              "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public }";
225          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
226          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
227          assert_eq!(expected, candidate.to_string());
228          assert_eq!("", remainder);
229          Ok(())
230      }
231  
232      #[test]
233      fn test_parse_with_version() -> Result<()> {
234          // Sanity check.
235          let expected = r"{
236    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
237    _nonce: 0group.public,
238    _version: 0u8.public
239  }";
240          let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public, _version: 0u8.public }";
241          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
242          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
243          assert_eq!(expected, candidate.to_string());
244          assert_eq!("", remainder);
245  
246          // Sanity check.
247          let expected = r"{
248    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
249    _nonce: 0group.public,
250    _version: 1u8.public
251  }";
252          let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public, _version: 1u8.public }";
253          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
254          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
255          assert_eq!(expected, candidate.to_string());
256          assert_eq!("", remainder);
257          Ok(())
258      }
259  
260      #[test]
261      fn test_parse_without_data_entries() -> Result<()> {
262          // Sanity check.
263          let expected = r"{
264    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
265    _nonce: 0group.public,
266    _version: 0u8.public
267  }";
268          let given =
269              "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public }";
270          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
271          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
272          assert_eq!(expected, candidate.to_string());
273          assert_eq!("", remainder);
274          Ok(())
275      }
276  
277      #[test]
278      fn test_parse_with_literal_entry() -> Result<()> {
279          let expected = r"{
280    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public,
281    foo: 5u8.constant,
282    _nonce: 0group.public,
283    _version: 0u8.public
284  }";
285          let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, foo: 5u8.constant, _nonce: 0group.public }";
286          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
287          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
288          assert_eq!(expected, candidate.to_string());
289          assert_eq!("", remainder);
290          Ok(())
291      }
292  
293      #[test]
294      fn test_parse_with_struct_entry() -> Result<()> {
295          let expected = r"{
296    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public,
297    foo: 5u8.public,
298    bar: {
299      baz: 6u8.constant,
300      qux: 7u8.constant
301    },
302    quux: 8u8.private,
303    corge: {
304      grault: 9u8.constant,
305      garply: {
306        waldo: 10u8.constant,
307        fred: 11u8.constant
308      }
309    },
310    xyzzy: {
311      thud: 12u8.public
312    },
313    _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public,
314    _version: 0u8.public
315  }";
316          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
317          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
318          assert_eq!(expected, candidate.to_string());
319          assert_eq!("", remainder);
320  
321          let expected = r"{
322    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
323    foo: {
324      bar: 0u128.private
325    },
326    baz: {
327      quine: {
328        flop: 0u64.private
329      },
330      slice: 0u16.private,
331      flag: true.private,
332      square: {
333        first: 0u128.private,
334        second: 1u128.private,
335        third: 2u128.private,
336        fourth: 3u128.private
337      }
338    },
339    _nonce: 0group.public,
340    _version: 0u8.public
341  }";
342          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
343          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
344          assert_eq!(expected, candidate.to_string());
345          assert_eq!("", remainder);
346  
347          let expected = r"{
348    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
349    c: {
350      c: {
351        a: 0u8.private,
352        b: 1u8.private
353      },
354      d: {
355        a: 0u8.private,
356        b: 1u8.private
357      }
358    },
359    d: {
360      c: {
361        a: 0u8.private,
362        b: 1u8.private
363      },
364      d: {
365        a: 0u8.private,
366        b: 1u8.private
367      }
368    },
369    _nonce: 8102307625287186026775464343238779600702564007094834161216556016558567413871group.public,
370    _version: 0u8.public
371  }";
372          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
373          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
374          assert_eq!(expected, candidate.to_string());
375          assert_eq!("", remainder);
376  
377          Ok(())
378      }
379  
380      #[test]
381      fn test_parse_with_array_entry() -> Result<()> {
382          let expected = r"{
383    owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public,
384    foo: 5u8.public,
385    bar: [
386      6u8.private,
387      7u8.private,
388      8u8.private
389    ],
390    _nonce: 0group.public,
391    _version: 0u8.public
392  }";
393          let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
394          println!("\nExpected: {expected}\n\nFound: {candidate}\n");
395          assert_eq!(expected, candidate.to_string());
396          assert_eq!("", remainder);
397          Ok(())
398      }
399  
400      #[test]
401      fn test_parse_fails() -> Result<()> {
402          // Missing owner.
403          let expected = "{ foo: 5u8.private, _nonce: 0group.public }";
404          assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
405  
406          // Missing nonce.
407          let expected =
408              "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, foo: 5u8.private }";
409          assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
410  
411          // Entry 'd' contains members with different visibility.
412          let expected = r"{
413      owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private,
414      a: true.private,
415      b: 123456789field.private,
416      c: 0group.private,
417      d: {
418          e: true.private,
419          f: 123456789field.public,
420          g: 0group.private
421      },
422      _nonce: 0group.public,
423      _version: 0u8.public
424  }";
425          assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
426          Ok(())
427      }
428  }