snarkvm_console_program/data/future/
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 Future<N> {
19    /// Parses a string into a future value.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        // Parse the future from the string.
23        Self::parse_internal(string, 0)
24    }
25}
26
27impl<N: Network> Future<N> {
28    /// Parses an array of future arguments: `[arg_0, ..., arg_1]`, while tracking the depth of the data.
29    fn parse_arguments(string: &str, depth: usize) -> ParserResult<Vec<Argument<N>>> {
30        // Parse the whitespace and comments from the string.
31        let (string, _) = Sanitizer::parse(string)?;
32        // Parse the "[" from the string.
33        let (string, _) = tag("[")(string)?;
34        // Parse the whitespace from the string.
35        let (string, _) = Sanitizer::parse(string)?;
36        // Parse the members.
37        let (string, arguments) = separated_list0(
38            pair(pair(Sanitizer::parse_whitespaces, tag(",")), Sanitizer::parse),
39            alt((
40                map(|input| Self::parse_internal(input, depth + 1), Argument::Future),
41                map(Plaintext::parse, Argument::Plaintext),
42            )),
43        )(string)?;
44        // Parse the whitespace and comments from the string.
45        let (string, _) = Sanitizer::parse(string)?;
46        // Parse the ']' from the string.
47        let (string, _) = tag("]")(string)?;
48        // Output the plaintext.
49        Ok((string, arguments))
50    }
51
52    /// Parses a string into a future value, while tracking the depth of the data.
53    #[inline]
54    fn parse_internal(string: &str, depth: usize) -> ParserResult<Self> {
55        // Ensure that the depth is within the maximum limit.
56        // Note: `N::MAX_DATA_DEPTH` is an upper bound on the number of nested futures.
57        //  The true maximum is defined by `Transaction::<N>::MAX_TRANSITIONS`, however, that object is not accessible in this crate.
58        //  In practice, `MAX_DATA_DEPTH` is 32, while `MAX_TRANSITIONS` is 31.
59        if depth > N::MAX_DATA_DEPTH {
60            return map_res(take(0usize), |_| {
61                Err(error(format!("Found a future that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
62            })(string);
63        }
64        // Parse the whitespace and comments from the string.
65        let (string, _) = Sanitizer::parse(string)?;
66        // Parse the "{" from the string.
67        let (string, _) = tag("{")(string)?;
68
69        // Parse the whitespace and comments from the string.
70        let (string, _) = Sanitizer::parse(string)?;
71        // Parse the "program_id" from the string.
72        let (string, _) = tag("program_id")(string)?;
73        // Parse the whitespace from the string.
74        let (string, _) = Sanitizer::parse_whitespaces(string)?;
75        // Parse the ":" from the string.
76        let (string, _) = tag(":")(string)?;
77        // Parse the whitespace from the string.
78        let (string, _) = Sanitizer::parse_whitespaces(string)?;
79        // Parse the program ID from the string.
80        let (string, program_id) = ProgramID::parse(string)?;
81        // Parse the whitespace from the string.
82        let (string, _) = Sanitizer::parse_whitespaces(string)?;
83        // Parse the "," from the string.
84        let (string, _) = tag(",")(string)?;
85
86        // Parse the whitespace and comments from the string.
87        let (string, _) = Sanitizer::parse(string)?;
88        // Parse the "function_name" from the string.
89        let (string, _) = tag("function_name")(string)?;
90        // Parse the whitespace from the string.
91        let (string, _) = Sanitizer::parse_whitespaces(string)?;
92        // Parse the ":" from the string.
93        let (string, _) = tag(":")(string)?;
94        // Parse the whitespace from the string.
95        let (string, _) = Sanitizer::parse_whitespaces(string)?;
96        // Parse the function name from the string.
97        let (string, function_name) = Identifier::parse(string)?;
98        // Parse the whitespace from the string.
99        let (string, _) = Sanitizer::parse_whitespaces(string)?;
100        // Parse the "," from the string.
101        let (string, _) = tag(",")(string)?;
102
103        // Parse the whitespace and comments from the string.
104        let (string, _) = Sanitizer::parse(string)?;
105        // Parse the "arguments" from the string.
106        let (string, _) = tag("arguments")(string)?;
107        // Parse the whitespace from the string.
108        let (string, _) = Sanitizer::parse_whitespaces(string)?;
109        // Parse the ":" from the string.
110        let (string, _) = tag(":")(string)?;
111        // Parse the whitespace from the string.
112        let (string, _) = Sanitizer::parse_whitespaces(string)?;
113        // Parse the arguments from the string.
114        let (string, arguments) = Self::parse_arguments(string, depth)?;
115
116        // Parse the whitespace and comments from the string.
117        let (string, _) = Sanitizer::parse(string)?;
118        // Parse the "}" from the string.
119        let (string, _) = tag("}")(string)?;
120
121        Ok((string, Self::new(program_id, function_name, arguments)))
122    }
123}
124
125impl<N: Network> FromStr for Future<N> {
126    type Err = Error;
127
128    /// Returns a future from a string literal.
129    fn from_str(string: &str) -> Result<Self> {
130        match Self::parse(string) {
131            Ok((remainder, object)) => {
132                // Ensure the remainder is empty.
133                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
134                // Return the object.
135                Ok(object)
136            }
137            Err(error) => bail!("Failed to parse string. {error}"),
138        }
139    }
140}
141
142impl<N: Network> Debug for Future<N> {
143    /// Prints the future as a string.
144    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
145        Display::fmt(self, f)
146    }
147}
148
149impl<N: Network> Display for Future<N> {
150    /// Prints the future as a string.
151    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
152        self.fmt_internal(f, 0)
153    }
154}
155
156impl<N: Network> Future<N> {
157    /// Prints the future with the given indentation depth.
158    fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
159        /// The number of spaces to indent.
160        const INDENT: usize = 2;
161
162        // Print the opening brace.
163        write!(f, "{{")?;
164
165        // Print the program ID.
166        write!(
167            f,
168            "\n{:indent$}program_id: {program_id},",
169            "",
170            indent = (depth + 1) * INDENT,
171            program_id = self.program_id()
172        )?;
173        // Print the function name.
174        write!(
175            f,
176            "\n{:indent$}function_name: {function_name},",
177            "",
178            indent = (depth + 1) * INDENT,
179            function_name = self.function_name()
180        )?;
181        // Print the arguments.
182        // If the arguments are empty, print an empty array.
183        if self.arguments.is_empty() {
184            write!(f, "\n{:indent$}arguments: []", "", indent = (depth + 1) * INDENT)?;
185        } else {
186            write!(f, "\n{:indent$}arguments: [", "", indent = (depth + 1) * INDENT)?;
187            self.arguments.iter().enumerate().try_for_each(|(i, argument)| {
188                match argument {
189                    Argument::Plaintext(plaintext) => match i == self.arguments.len() - 1 {
190                        true => {
191                            // Print the last argument without a comma.
192                            write!(
193                                f,
194                                "\n{:indent$}{plaintext}",
195                                "",
196                                indent = (depth + 2) * INDENT,
197                                plaintext = plaintext
198                            )
199                        }
200                        // Print the argument with a comma.
201                        false => {
202                            write!(
203                                f,
204                                "\n{:indent$}{plaintext},",
205                                "",
206                                indent = (depth + 2) * INDENT,
207                                plaintext = plaintext
208                            )
209                        }
210                    },
211                    Argument::Future(future) => {
212                        // Print a newline.
213                        write!(f, "\n{:indent$}", "", indent = (depth + 2) * INDENT)?;
214                        // Print the argument.
215                        future.fmt_internal(f, depth + 2)?;
216                        // Print the closing brace.
217                        match i == self.arguments.len() - 1 {
218                            // Print the last member without a comma.
219                            true => write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT),
220                            // Print the member with a comma.
221                            false => write!(f, ","),
222                        }
223                    }
224                }
225            })?;
226            // Print the closing bracket.
227            write!(f, "\n{:indent$}]", "", indent = (depth + 1) * INDENT)?;
228        }
229
230        // Print the closing brace.
231        write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238    use snarkvm_console_network::MainnetV0;
239
240    type CurrentNetwork = MainnetV0;
241
242    #[test]
243    fn test_parse_future() -> Result<()> {
244        // No argument case.
245        let expected = r"{
246  program_id: credits.aleo,
247  function_name: transfer,
248  arguments: []
249}";
250        let (remainder, candidate) =
251            Future::<CurrentNetwork>::parse("{ program_id: credits.aleo, function_name: transfer, arguments: [] }")?;
252        assert!(remainder.is_empty());
253        assert_eq!(expected, candidate.to_string());
254        assert_eq!("", remainder);
255
256        // Literal arguments.
257        let expected = r"{
258  program_id: credits.aleo,
259  function_name: transfer_public_to_private,
260  arguments: [
261    aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2,
262    100000000u64
263  ]
264}";
265        let (remainder, candidate) = Future::<CurrentNetwork>::parse(
266            "{ program_id: credits.aleo, function_name: transfer_public_to_private, arguments: [ aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2, 100000000u64 ] }",
267        )?;
268        assert!(remainder.is_empty());
269        assert_eq!(expected, candidate.to_string());
270        assert_eq!("", remainder);
271
272        Ok(())
273    }
274
275    #[test]
276    fn test_deeply_nested_future() {
277        // A helper function to iteratively create a deeply nested future.
278        fn create_nested_future(depth: usize) -> String {
279            // Define the base case.
280            let root = r"{
281                program_id: foo.aleo,
282                function_name: bar,
283                arguments: []
284            }";
285            // Define the prefix and suffix for the nested future.
286            let prefix = r"{
287                program_id: foo.aleo,
288                function_name: bar,
289                arguments: ["
290                .repeat(depth);
291            let suffix = r"]}".repeat(depth);
292            // Concatenate the prefix, root, and suffix to create the nested future.
293            format!("{}{}{}", prefix, root, suffix)
294        }
295
296        // A helper function to test the parsing of a deeply nested future.
297        fn run_test(depth: usize, expected_error: bool) {
298            // Create the nested future string.
299            let nested_future_string = create_nested_future(depth);
300            // Parse the nested future.
301            let result = Future::<CurrentNetwork>::parse(&nested_future_string);
302            // Check if the result is an error.
303            match expected_error {
304                true => {
305                    assert!(result.is_err());
306                    return;
307                }
308                false => assert!(result.is_ok()),
309            };
310            // Unwrap the result.
311            let (remainder, candidate) = result.unwrap();
312            // Ensure the remainder is empty.
313            assert!(
314                remainder.is_empty(),
315                "Failed to parse deeply nested future. Found invalid character in: \"{remainder}\""
316            );
317            // Strip the expected string of whitespace.
318            let expected = nested_future_string.replace("\n", "").replace(" ", "").replace("\t", "");
319            // Strip the candidate string of whitespace.
320            let candidate_str = candidate.to_string().replace("\n", "").replace(" ", "").replace("\t", "");
321            // Ensure the expected and candidate strings are equal.
322            assert_eq!(expected, candidate_str, "Expected: {expected}, Candidate: {candidate_str}");
323        }
324
325        // Initialize a set of depths to test.
326        let mut depths = (0usize..100).collect_vec();
327        depths.extend((100..1000).step_by(100));
328        depths.extend((1000..10000).step_by(1000));
329        depths.extend((10000..100000).step_by(10000));
330
331        // For each depth, test the parsing of a deeply nested future.
332        for depth in depths {
333            run_test(depth, depth > CurrentNetwork::MAX_DATA_DEPTH);
334        }
335    }
336}