snarkvm_console_program/data/record/entry/
parse.rs

1// Copyright 2024 Aleo Network Foundation
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`.
31        fn parse_pair<N: Network>(string: &str) -> 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                parse_literal,
46                // Parse a struct.
47                parse_struct,
48                // Parse an array.
49                parse_array,
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`.
58        fn parse_literal<N: Network>(string: &str) -> ParserResult<(Plaintext<N>, Mode)> {
59            alt((
60                map(pair(Literal::parse, tag(".constant")), |(literal, _)| (Plaintext::from(literal), Mode::Constant)),
61                map(pair(Literal::parse, tag(".public")), |(literal, _)| (Plaintext::from(literal), Mode::Public)),
62                map(pair(Literal::parse, tag(".private")), |(literal, _)| (Plaintext::from(literal), Mode::Private)),
63            ))(string)
64        }
65
66        /// Parses an entry as a struct: `{ identifier_0: plaintext_0.visibility, ..., identifier_n: plaintext_n.visibility }`.
67        /// Observe the `visibility` is the same for all members of the plaintext value.
68        fn parse_struct<N: Network>(string: &str) -> ParserResult<(Plaintext<N>, Mode)> {
69            // Parse the whitespace and comments from the string.
70            let (string, _) = Sanitizer::parse(string)?;
71            // Parse the "{" from the string.
72            let (string, _) = tag("{")(string)?;
73            // Parse the whitespace from the string.
74            let (string, _) = Sanitizer::parse_whitespaces(string)?;
75            // Parse the members.
76            let (string, (members, mode)) = map_res(separated_list1(tag(","), parse_pair), |members: Vec<_>| {
77                // Ensure the members has no duplicate names.
78                if has_duplicates(members.iter().map(|(name, ..)| name)) {
79                    return Err(error("Duplicate member in struct"));
80                }
81                // Ensure the members all have the same visibility.
82                let mode = members.iter().map(|(_, _, mode)| mode).dedup().collect::<Vec<_>>();
83                let mode = match mode.len() == 1 {
84                    true => *mode[0],
85                    false => return Err(error("Members of struct in entry have different visibilities")),
86                };
87                // Ensure the number of structs is within the maximum limit.
88                match members.len() <= N::MAX_STRUCT_ENTRIES {
89                    // Return the members and the visibility.
90                    true => Ok((members.into_iter().map(|(i, p, _)| (i, p)).collect::<Vec<_>>(), mode)),
91                    false => Err(error(format!("Found a struct that exceeds size ({})", members.len()))),
92                }
93            })(string)?;
94            // Parse the whitespace and comments from the string.
95            let (string, _) = Sanitizer::parse(string)?;
96            // Parse the '}' from the string.
97            let (string, _) = tag("}")(string)?;
98            // Output the plaintext and visibility.
99            Ok((string, (Plaintext::Struct(IndexMap::from_iter(members), Default::default()), mode)))
100        }
101
102        /// Parses an entry as an array: `[plaintext_0.visibility, ..., plaintext_n.visibility]`.
103        /// Observe the `visibility` is the same for all members of the plaintext value.
104        fn parse_array<N: Network>(string: &str) -> ParserResult<(Plaintext<N>, Mode)> {
105            // Parse the whitespace and comments from the string.
106            let (string, _) = Sanitizer::parse(string)?;
107            // Parse the "[" from the string.
108            let (string, _) = tag("[")(string)?;
109            // Parse the whitespace from the string.
110            let (string, _) = Sanitizer::parse_whitespaces(string)?;
111            // Parse the members.
112            let (string, (elements, mode)) = map_res(
113                separated_list1(
114                    pair(Sanitizer::parse_whitespaces, pair(tag(","), Sanitizer::parse_whitespaces)),
115                    alt((parse_literal, parse_struct, parse_array)),
116                ),
117                |members: Vec<(Plaintext<N>, Mode)>| {
118                    // Ensure the members all have the same visibility.
119                    let mode = members.iter().map(|(_, mode)| mode).dedup().collect::<Vec<_>>();
120                    let mode = match mode.len() == 1 {
121                        true => *mode[0],
122                        false => return Err(error("Members of an array have different visibilities")),
123                    };
124                    // Ensure the number of array elements is within the maximum limit.
125                    match members.len() <= N::MAX_ARRAY_ELEMENTS {
126                        // Return the members and the visibility.
127                        true => Ok((members.into_iter().map(|(p, _)| p).collect::<Vec<_>>(), mode)),
128                        false => Err(error(format!("Found an array that exceeds size ({})", members.len()))),
129                    }
130                },
131            )(string)?;
132            // Parse the whitespace and comments from the string.
133            let (string, _) = Sanitizer::parse(string)?;
134            // Parse the ']' from the string.
135            let (string, _) = tag("]")(string)?;
136            // Output the plaintext and visibility.
137            Ok((string, (Plaintext::Array(elements, Default::default()), mode)))
138        }
139
140        // Parse the whitespace from the string.
141        let (string, _) = Sanitizer::parse_whitespaces(string)?;
142        // Parse to determine the entry (order matters).
143        let (string, (plaintext, mode)) = alt((
144            // Parse a literal.
145            parse_literal,
146            // Parse a struct.
147            parse_struct,
148            // Parse an array.
149            parse_array,
150        ))(string)?;
151
152        // Return the entry.
153        match mode {
154            Mode::Constant => Ok((string, Entry::Constant(plaintext))),
155            Mode::Public => Ok((string, Entry::Public(plaintext))),
156            Mode::Private => Ok((string, Entry::Private(plaintext))),
157        }
158    }
159}
160
161impl<N: Network> FromStr for Entry<N, Plaintext<N>> {
162    type Err = Error;
163
164    /// Returns the entry from a string literal.
165    fn from_str(string: &str) -> Result<Self> {
166        match Self::parse(string) {
167            Ok((remainder, object)) => {
168                // Ensure the remainder is empty.
169                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
170                // Return the object.
171                Ok(object)
172            }
173            Err(error) => bail!("Failed to parse string. {error}"),
174        }
175    }
176}
177
178impl<N: Network> Debug for Entry<N, Plaintext<N>> {
179    /// Prints the entry as a string.
180    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
181        Display::fmt(self, f)
182    }
183}
184
185impl<N: Network> Display for Entry<N, Plaintext<N>> {
186    /// Prints the entry as a string.
187    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
188        self.fmt_internal(f, 0)
189    }
190}
191
192impl<N: Network> Entry<N, Plaintext<N>> {
193    /// Prints the entry with the given indentation depth.
194    pub(in crate::data::record) fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
195        /// The number of spaces to indent.
196        const INDENT: usize = 2;
197
198        let (plaintext, visibility) = match self {
199            Self::Constant(constant) => (constant, "constant"),
200            Self::Public(public) => (public, "public"),
201            Self::Private(private) => (private, "private"),
202        };
203
204        match plaintext {
205            // Prints the literal, i.e. 10field.public
206            Plaintext::Literal(literal, ..) => {
207                write!(f, "{:indent$}{literal}.{visibility}", "", indent = depth * INDENT)
208            }
209            // Prints the struct, i.e. { first: 10i64.private, second: 198u64.private }
210            Plaintext::Struct(struct_, ..) => {
211                // Print the opening brace.
212                write!(f, "{{")?;
213                // Print the members.
214                struct_.iter().enumerate().try_for_each(|(i, (name, plaintext))| {
215                    match plaintext {
216                        #[rustfmt::skip]
217                        Plaintext::Literal(literal, ..) => match i == struct_.len() - 1 {
218                            true => {
219                                // Print the last member without a comma.
220                                write!(f, "\n{:indent$}{name}: {literal}.{visibility}", "", indent = (depth + 1) * INDENT)?;
221                                // Print the closing brace.
222                                write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
223                            }
224                            // Print the member with a comma.
225                            false => write!(f, "\n{:indent$}{name}: {literal}.{visibility},", "", indent = (depth + 1) * INDENT),
226                        },
227                        Plaintext::Struct(..) | Plaintext::Array(..) => {
228                            // Print the member name.
229                            write!(f, "\n{:indent$}{name}: ", "", indent = (depth + 1) * INDENT)?;
230                            // Print the member.
231                            match self {
232                                Self::Constant(..) => Self::Constant(plaintext.clone()).fmt_internal(f, depth + 1)?,
233                                Self::Public(..) => Self::Public(plaintext.clone()).fmt_internal(f, depth + 1)?,
234                                Self::Private(..) => Self::Private(plaintext.clone()).fmt_internal(f, depth + 1)?,
235                            }
236                            // Print the closing brace.
237                            match i == struct_.len() - 1 {
238                                // If this inner struct is the last member of the outer struct, print the closing brace of the outer struct.
239                                true => write!(f, "\n{:indent$}}}", "", indent = depth * INDENT),
240                                // Otherwise, print a comma after the inner struct, because the outer struct has more members after this one.
241                                false => write!(f, ","),
242                            }
243                        },
244                    }
245                })
246            }
247            // Prints the array, i.e. [ 10u64.public, 198u64.private ]
248            Plaintext::Array(array, ..) => {
249                // Print the opening bracket.
250                write!(f, "[")?;
251                // Print the members.
252                array.iter().enumerate().try_for_each(|(i, plaintext)| {
253                    match plaintext {
254                        #[rustfmt::skip]
255                        Plaintext::Literal(literal, ..) => match i == array.len() - 1 {
256                            true => {
257                                // Print the last member without a comma.
258                                write!(f, "\n{:indent$}{literal}.{visibility}", "", indent = (depth + 1) * INDENT)?;
259                                // Print the closing brace.
260                                write!(f, "\n{:indent$}]", "", indent = depth * INDENT)
261                            }
262                            // Print the member with a comma.
263                            false => write!(f, "\n{:indent$}{literal}.{visibility},", "", indent = (depth + 1) * INDENT),
264                        },
265                        Plaintext::Struct(..) | Plaintext::Array(..) => {
266                            // Print a new line.
267                            write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT)?;
268                            // Print the member.
269                            match self {
270                                Self::Constant(..) => Self::Constant(plaintext.clone()).fmt_internal(f, depth + 1)?,
271                                Self::Public(..) => Self::Public(plaintext.clone()).fmt_internal(f, depth + 1)?,
272                                Self::Private(..) => Self::Private(plaintext.clone()).fmt_internal(f, depth + 1)?,
273                            }
274                            // Print the closing brace.
275                            match i == array.len() - 1 {
276                                // If this inner struct is the last member of the outer struct, print the closing bracket of the outer vector.
277                                true => write!(f, "\n{:indent$}]", "", indent = depth * INDENT),
278                                // Otherwise, print a comma after the inner struct, because the outer vector has more members after this one.
279                                false => write!(f, ","),
280                            }
281                        },
282                    }
283                })
284            }
285        }
286    }
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292    use snarkvm_console_network::MainnetV0;
293
294    type CurrentNetwork = MainnetV0;
295
296    #[test]
297    fn test_parse() -> Result<()> {
298        // Sanity check.
299        let expected = r"{
300  foo: 5u8.private
301}";
302        let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse("{ foo: 5u8.private }")?;
303        assert_eq!(expected, candidate.to_string());
304        assert_eq!("", remainder);
305
306        let expected = r"{
307  foo: 5u8.public,
308  bar: {
309    baz: 10field.public,
310    qux: {
311      quux: {
312        corge: {
313          grault: {
314            garply: {
315              waldo: {
316                fred: {
317                  plugh: {
318                    xyzzy: {
319                      thud: true.public
320                    }
321                  }
322                }
323              }
324            }
325          }
326        }
327      }
328    }
329  }
330}";
331        let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
332            "{ foo: 5u8.public, bar: { baz: 10field.public, qux: {quux:{corge :{grault:  {garply:{waldo:{fred:{plugh:{xyzzy: { thud: true.public}} }}}  }}}}}}",
333        )?;
334        println!("\nExpected: {expected}\n\nFound: {candidate}\n");
335        assert_eq!(expected, candidate.to_string());
336        assert_eq!("", remainder);
337
338        // Test an array of literals.
339        let expected = r"[
340  5u8.private,
341  10u8.private,
342  15u8.private
343]";
344        let (remainder, candidate) =
345            Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse("[ 5u8.private, 10u8.private, 15u8.private ]")?;
346        assert_eq!(expected, candidate.to_string());
347        assert_eq!("", remainder);
348
349        // Test an array of structs.
350        let expected = r"[
351  {
352    foo: 5u8.public
353  },
354  {
355    bar: 10u8.public
356  },
357  {
358    baz: 15u8.public
359  }
360]";
361        let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
362            "[ { foo: 5u8.public }, { bar: 10u8.public }, { baz: 15u8.public } ]",
363        )?;
364        assert_eq!(expected, candidate.to_string());
365        assert_eq!("", remainder);
366
367        // Test a struct with arrays.
368        let expected = r"{
369  foo: [
370    5u8.public,
371    10u8.public,
372    15u8.public
373  ],
374  bar: [
375    {
376      foo: 5u8.public
377    },
378    {
379      bar: 10u8.public
380    },
381    {
382      baz: 15u8.public
383    }
384  ]
385}";
386        let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
387            "{ foo: [ 5u8.public, 10u8.public, 15u8.public ], bar: [ { foo: 5u8.public }, { bar: 10u8.public }, { baz: 15u8.public } ] }",
388        )?;
389        assert_eq!(expected, candidate.to_string());
390        assert_eq!("", remainder);
391
392        Ok(())
393    }
394}