Skip to main content

snarkvm_console_program/data/dynamic/future/
parse.rs

1// Copyright (c) 2019-2026 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 DynamicFuture<N> {
19    /// Parses a string into a dynamic future.
20    ///
21    /// Supports two formats:
22    /// - Human-readable: `{ _program_id: foo.aleo, _function_name: bar, _checksum: 0field }`
23    /// - Raw field: `{ _program_name: 0field, _program_network: 0field, _function_name: 0field, _checksum: 0field }`
24    #[inline]
25    fn parse(string: &str) -> ParserResult<Self> {
26        // Try to parse the human-readable format first.
27        if let Ok(result) = Self::parse_human_readable(string) {
28            return Ok(result);
29        }
30        // Fall back to raw field format.
31        Self::parse_raw_fields(string)
32    }
33}
34
35impl<N: Network> DynamicFuture<N> {
36    /// Parses the human-readable format: `{ _program_id: foo.aleo, _function_name: bar, _checksum: 0field }`.
37    fn parse_human_readable(string: &str) -> ParserResult<Self> {
38        // Parse the whitespace and comments from the string.
39        let (string, _) = Sanitizer::parse(string)?;
40        // Parse the "{" from the string.
41        let (string, _) = tag("{")(string)?;
42
43        // Parse the whitespace and comments from the string.
44        let (string, _) = Sanitizer::parse(string)?;
45        // Parse the "_program_id" from the string.
46        let (string, _) = tag("_program_id")(string)?;
47        // Parse the whitespace from the string.
48        let (string, _) = Sanitizer::parse_whitespaces(string)?;
49        // Parse the ":" from the string.
50        let (string, _) = tag(":")(string)?;
51        // Parse the whitespace from the string.
52        let (string, _) = Sanitizer::parse_whitespaces(string)?;
53        // Parse the program ID from the string.
54        let (string, program_id) = ProgramID::parse(string)?;
55        // Parse the whitespace from the string.
56        let (string, _) = Sanitizer::parse_whitespaces(string)?;
57        // Parse the "," from the string.
58        let (string, _) = tag(",")(string)?;
59
60        // Parse the whitespace and comments from the string.
61        let (string, _) = Sanitizer::parse(string)?;
62        // Parse the "_function_name" from the string.
63        let (string, _) = tag("_function_name")(string)?;
64        // Parse the whitespace from the string.
65        let (string, _) = Sanitizer::parse_whitespaces(string)?;
66        // Parse the ":" from the string.
67        let (string, _) = tag(":")(string)?;
68        // Parse the whitespace from the string.
69        let (string, _) = Sanitizer::parse_whitespaces(string)?;
70        // Parse the function name from the string.
71        let (string, function_name) = Identifier::parse(string)?;
72        // Parse the whitespace from the string.
73        let (string, _) = Sanitizer::parse_whitespaces(string)?;
74        // Parse the "," from the string.
75        let (string, _) = tag(",")(string)?;
76
77        // Parse the whitespace and comments from the string.
78        let (string, _) = Sanitizer::parse(string)?;
79        // Parse the "_checksum" from the string.
80        let (string, _) = tag("_checksum")(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        // Parse the whitespace from the string.
86        let (string, _) = Sanitizer::parse_whitespaces(string)?;
87        // Parse the argument checksum from the string.
88        let (string, checksum) = Field::parse(string)?;
89
90        // Parse the whitespace and comments from the string.
91        let (string, _) = Sanitizer::parse(string)?;
92        // Parse the "}" from the string.
93        let (string, _) = tag("}")(string)?;
94
95        // Convert to field representation.
96        // Safe: identifiers are validated to fit within 31 bytes, which always fit in a 253-bit field element.
97        let program_name = program_id.name().to_field().expect("identifier always fits in a field element");
98        let program_network = program_id.network().to_field().expect("identifier always fits in a field element");
99        let function_name_field = function_name.to_field().expect("identifier always fits in a field element");
100
101        Ok((string, Self::new_unchecked(program_name, program_network, function_name_field, checksum, None)))
102    }
103
104    /// Parses the raw field format: `{ _program_name: 0field, _program_network: 0field, _function_name: 0field, _checksum: 0field }`.
105    fn parse_raw_fields(string: &str) -> ParserResult<Self> {
106        // Parse the whitespace and comments from the string.
107        let (string, _) = Sanitizer::parse(string)?;
108        // Parse the "{" from the string.
109        let (string, _) = tag("{")(string)?;
110
111        // Parse the whitespace and comments from the string.
112        let (string, _) = Sanitizer::parse(string)?;
113        // Parse the "_program_name" from the string.
114        let (string, _) = tag("_program_name")(string)?;
115        // Parse the whitespace from the string.
116        let (string, _) = Sanitizer::parse_whitespaces(string)?;
117        // Parse the ":" from the string.
118        let (string, _) = tag(":")(string)?;
119        // Parse the whitespace from the string.
120        let (string, _) = Sanitizer::parse_whitespaces(string)?;
121        // Parse the program name from the string.
122        let (string, program_name) = Field::parse(string)?;
123        // Parse the whitespace from the string.
124        let (string, _) = Sanitizer::parse_whitespaces(string)?;
125        // Parse the "," from the string.
126        let (string, _) = tag(",")(string)?;
127
128        // Parse the whitespace and comments from the string.
129        let (string, _) = Sanitizer::parse(string)?;
130        // Parse the "_program_network" from the string.
131        let (string, _) = tag("_program_network")(string)?;
132        // Parse the whitespace from the string.
133        let (string, _) = Sanitizer::parse_whitespaces(string)?;
134        // Parse the ":" from the string.
135        let (string, _) = tag(":")(string)?;
136        // Parse the whitespace from the string.
137        let (string, _) = Sanitizer::parse_whitespaces(string)?;
138        // Parse the program network from the string.
139        let (string, program_network) = Field::parse(string)?;
140        // Parse the whitespace from the string.
141        let (string, _) = Sanitizer::parse_whitespaces(string)?;
142        // Parse the "," from the string.
143        let (string, _) = tag(",")(string)?;
144
145        // Parse the whitespace and comments from the string.
146        let (string, _) = Sanitizer::parse(string)?;
147        // Parse the "_function_name" from the string.
148        let (string, _) = tag("_function_name")(string)?;
149        // Parse the whitespace from the string.
150        let (string, _) = Sanitizer::parse_whitespaces(string)?;
151        // Parse the ":" from the string.
152        let (string, _) = tag(":")(string)?;
153        // Parse the whitespace from the string.
154        let (string, _) = Sanitizer::parse_whitespaces(string)?;
155        // Parse the function name from the string.
156        let (string, function_name) = Field::parse(string)?;
157        // Parse the whitespace from the string.
158        let (string, _) = Sanitizer::parse_whitespaces(string)?;
159        // Parse the "," from the string.
160        let (string, _) = tag(",")(string)?;
161
162        // Parse the whitespace and comments from the string.
163        let (string, _) = Sanitizer::parse(string)?;
164        // Parse the "_checksum" from the string.
165        let (string, _) = tag("_checksum")(string)?;
166        // Parse the whitespace from the string.
167        let (string, _) = Sanitizer::parse_whitespaces(string)?;
168        // Parse the ":" from the string.
169        let (string, _) = tag(":")(string)?;
170        // Parse the whitespace from the string.
171        let (string, _) = Sanitizer::parse_whitespaces(string)?;
172        // Parse the argument checksum from the string.
173        let (string, checksum) = Field::parse(string)?;
174
175        // Parse the whitespace and comments from the string.
176        let (string, _) = Sanitizer::parse(string)?;
177        // Parse the "}" from the string.
178        let (string, _) = tag("}")(string)?;
179
180        Ok((string, Self::new_unchecked(program_name, program_network, function_name, checksum, None)))
181    }
182}
183
184impl<N: Network> FromStr for DynamicFuture<N> {
185    type Err = Error;
186
187    /// Returns a future 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 DynamicFuture<N> {
202    /// Prints the future 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 DynamicFuture<N> {
209    /// Prints the future as a string.
210    ///
211    /// Attempts to display in human-readable format if the fields can be converted to identifiers.
212    /// Falls back to raw field format if conversion fails.
213    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
214        // Try to convert fields to identifiers for human-readable display.
215        let program_name_id = Identifier::<N>::from_field(&self.program_name);
216        let program_network_id = Identifier::<N>::from_field(&self.program_network);
217        let function_name_id = Identifier::<N>::from_field(&self.function_name);
218
219        // If all conversions succeed, display human-readable format.
220        if let (Ok(name), Ok(network), Ok(function)) = (program_name_id, program_network_id, function_name_id) {
221            if let Ok(program_id) = ProgramID::try_from((name, network)) {
222                return write!(
223                    f,
224                    "{{ _program_id: {program_id}, _function_name: {function}, _checksum: {} }}",
225                    self.checksum()
226                );
227            }
228        }
229
230        // Fall back to raw field format.
231        write!(
232            f,
233            "{{ _program_name: {}, _program_network: {}, _function_name: {}, _checksum: {} }}",
234            self.program_name(),
235            self.program_network(),
236            self.function_name(),
237            self.checksum(),
238        )
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use super::*;
245    use crate::{Argument, Future, Plaintext};
246    use snarkvm_console_network::MainnetV0;
247
248    use core::str::FromStr;
249
250    type CurrentNetwork = MainnetV0;
251
252    #[test]
253    fn test_parse_display_roundtrip() {
254        // Create a static future.
255        let future = Future::<CurrentNetwork>::new(
256            crate::ProgramID::from_str("test.aleo").unwrap(),
257            crate::Identifier::from_str("foo").unwrap(),
258            vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
259        );
260
261        // Convert to dynamic future.
262        let expected = DynamicFuture::from_future(&future).unwrap();
263
264        // Convert to string.
265        let expected_string = expected.to_string();
266
267        // Parse the string.
268        let candidate = DynamicFuture::<CurrentNetwork>::from_str(&expected_string).unwrap();
269
270        // Verify the fields match.
271        assert_eq!(expected.program_name(), candidate.program_name());
272        assert_eq!(expected.program_network(), candidate.program_network());
273        assert_eq!(expected.function_name(), candidate.function_name());
274        assert_eq!(expected.checksum(), candidate.checksum());
275    }
276
277    #[test]
278    fn test_parse_human_readable() {
279        // Parse a dynamic future from a human-readable string.
280        let string = "{ _program_id: test.aleo, _function_name: foo, _checksum: 0field }";
281        let (remainder, candidate) = DynamicFuture::<CurrentNetwork>::parse(string).unwrap();
282        assert!(remainder.is_empty());
283
284        // Verify the program ID and function name can be recovered.
285        let program_name = Identifier::<CurrentNetwork>::from_field(candidate.program_name()).unwrap();
286        let program_network = Identifier::<CurrentNetwork>::from_field(candidate.program_network()).unwrap();
287        let function_name = Identifier::<CurrentNetwork>::from_field(candidate.function_name()).unwrap();
288
289        assert_eq!(program_name.to_string(), "test");
290        assert_eq!(program_network.to_string(), "aleo");
291        assert_eq!(function_name.to_string(), "foo");
292        assert_eq!(*candidate.checksum(), Field::from_u64(0));
293    }
294
295    #[test]
296    fn test_parse_raw_fields() {
297        // Parse a dynamic future from a raw field string.
298        let string = "{ _program_name: 0field, _program_network: 0field, _function_name: 0field, _checksum: 0field }";
299        let (remainder, candidate) = DynamicFuture::<CurrentNetwork>::parse(string).unwrap();
300        assert!(remainder.is_empty());
301        assert_eq!(*candidate.program_name(), Field::from_u64(0));
302        assert_eq!(*candidate.program_network(), Field::from_u64(0));
303        assert_eq!(*candidate.function_name(), Field::from_u64(0));
304        assert_eq!(*candidate.checksum(), Field::from_u64(0));
305    }
306
307    #[test]
308    fn test_display_human_readable() {
309        // Create a static future.
310        let future = Future::<CurrentNetwork>::new(
311            crate::ProgramID::from_str("credits.aleo").unwrap(),
312            crate::Identifier::from_str("transfer").unwrap(),
313            vec![],
314        );
315
316        // Convert to dynamic future.
317        let dynamic = DynamicFuture::from_future(&future).unwrap();
318
319        // Check that the display uses human-readable format.
320        let display = dynamic.to_string();
321        assert!(display.contains("_program_id:"));
322        assert!(display.contains("credits.aleo"));
323        assert!(display.contains("_function_name:"));
324        assert!(display.contains("transfer"));
325        assert!(display.contains("_checksum:"));
326    }
327
328    #[test]
329    fn test_display_fallback_to_raw() {
330        // Create a dynamic future with invalid field values that cannot be converted to identifiers.
331        let dynamic = DynamicFuture::<CurrentNetwork>::new_unchecked(
332            Field::from_u64(u64::MAX),
333            Field::from_u64(u64::MAX),
334            Field::from_u64(u64::MAX),
335            Field::from_u64(0),
336            None,
337        );
338
339        // Check that the display falls back to raw field format.
340        let display = dynamic.to_string();
341        assert!(display.contains("_program_name:"));
342        assert!(display.contains("_program_network:"));
343        assert!(display.contains("_function_name:"));
344        assert!(display.contains("_checksum:"));
345    }
346}