snarkvm_console_program/data/record/entry/
parse.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM 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
16use super::*;
17
18impl<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
184impl<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
201impl<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
208impl<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
215impl<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)]
313mod tests {
314    use super::*;
315    use snarkvm_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}