1use super::*;
17
18impl<N: Network> Parser for Future<N> {
19 #[inline]
21 fn parse(string: &str) -> ParserResult<Self> {
22 Self::parse_internal(string, 0)
24 }
25}
26
27impl<N: Network> Future<N> {
28 fn parse_arguments(string: &str, depth: usize) -> ParserResult<Vec<Argument<N>>> {
30 let (string, _) = Sanitizer::parse(string)?;
32 let (string, _) = tag("[")(string)?;
34 let (string, _) = Sanitizer::parse(string)?;
36 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(DynamicFuture::parse, Argument::DynamicFuture),
42 map(Plaintext::parse, Argument::Plaintext),
43 )),
44 )(string)?;
45 let (string, _) = Sanitizer::parse(string)?;
47 let (string, _) = tag("]")(string)?;
49 Ok((string, arguments))
51 }
52
53 #[inline]
55 fn parse_internal(string: &str, depth: usize) -> ParserResult<Self> {
56 if depth > N::MAX_DATA_DEPTH {
61 return map_res(take(0usize), |_| {
62 Err(error(format!("Found a future that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
63 })(string);
64 }
65 let (string, _) = Sanitizer::parse(string)?;
67 let (string, _) = tag("{")(string)?;
69
70 let (string, _) = Sanitizer::parse(string)?;
72 let (string, _) = tag("program_id")(string)?;
74 let (string, _) = Sanitizer::parse_whitespaces(string)?;
76 let (string, _) = tag(":")(string)?;
78 let (string, _) = Sanitizer::parse_whitespaces(string)?;
80 let (string, program_id) = ProgramID::parse(string)?;
82 let (string, _) = Sanitizer::parse_whitespaces(string)?;
84 let (string, _) = tag(",")(string)?;
86
87 let (string, _) = Sanitizer::parse(string)?;
89 let (string, _) = tag("function_name")(string)?;
91 let (string, _) = Sanitizer::parse_whitespaces(string)?;
93 let (string, _) = tag(":")(string)?;
95 let (string, _) = Sanitizer::parse_whitespaces(string)?;
97 let (string, function_name) = Identifier::parse(string)?;
99 let (string, _) = Sanitizer::parse_whitespaces(string)?;
101 let (string, _) = tag(",")(string)?;
103
104 let (string, _) = Sanitizer::parse(string)?;
106 let (string, _) = tag("arguments")(string)?;
108 let (string, _) = Sanitizer::parse_whitespaces(string)?;
110 let (string, _) = tag(":")(string)?;
112 let (string, _) = Sanitizer::parse_whitespaces(string)?;
114 let (string, arguments) = Self::parse_arguments(string, depth)?;
116
117 let (string, _) = Sanitizer::parse(string)?;
119 let (string, _) = tag("}")(string)?;
121
122 Ok((string, Self::new(program_id, function_name, arguments)))
123 }
124}
125
126impl<N: Network> FromStr for Future<N> {
127 type Err = Error;
128
129 fn from_str(string: &str) -> Result<Self> {
131 match Self::parse(string) {
132 Ok((remainder, object)) => {
133 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
135 Ok(object)
137 }
138 Err(error) => bail!("Failed to parse string. {error}"),
139 }
140 }
141}
142
143impl<N: Network> Debug for Future<N> {
144 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
146 Display::fmt(self, f)
147 }
148}
149
150impl<N: Network> Display for Future<N> {
151 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
153 self.fmt_internal(f, 0)
154 }
155}
156
157impl<N: Network> Future<N> {
158 fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
160 const INDENT: usize = 2;
162
163 write!(f, "{{")?;
165
166 write!(
168 f,
169 "\n{:indent$}program_id: {program_id},",
170 "",
171 indent = (depth + 1) * INDENT,
172 program_id = self.program_id()
173 )?;
174 write!(
176 f,
177 "\n{:indent$}function_name: {function_name},",
178 "",
179 indent = (depth + 1) * INDENT,
180 function_name = self.function_name()
181 )?;
182 if self.arguments.is_empty() {
185 write!(f, "\n{:indent$}arguments: []", "", indent = (depth + 1) * INDENT)?;
186 } else {
187 write!(f, "\n{:indent$}arguments: [", "", indent = (depth + 1) * INDENT)?;
188 self.arguments.iter().enumerate().try_for_each(|(i, argument)| {
189 match argument {
190 Argument::Plaintext(plaintext) => match i == self.arguments.len() - 1 {
191 true => {
192 write!(f, "\n{:indent$}{plaintext}", "", indent = (depth + 2) * INDENT)
194 }
195 false => {
197 write!(f, "\n{:indent$}{plaintext},", "", indent = (depth + 2) * INDENT)
198 }
199 },
200 Argument::Future(future) => {
201 write!(f, "\n{:indent$}", "", indent = (depth + 2) * INDENT)?;
203 future.fmt_internal(f, depth + 2)?;
205 match i == self.arguments.len() - 1 {
207 true => write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT),
209 false => write!(f, ","),
211 }
212 }
213 Argument::DynamicFuture(dynamic_future) => {
214 match i == self.arguments.len() - 1 {
215 true => {
216 write!(f, "\n{:indent$}{dynamic_future}", "", indent = (depth + 2) * INDENT)
218 }
219 false => write!(f, "\n{:indent$}{dynamic_future},", "", indent = (depth + 2) * INDENT),
221 }
222 }
223 }
224 })?;
225 write!(f, "\n{:indent$}]", "", indent = (depth + 1) * INDENT)?;
227 }
228
229 write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use snarkvm_console_network::MainnetV0;
238
239 use core::str::FromStr;
240
241 type CurrentNetwork = MainnetV0;
242
243 #[test]
244 fn test_parse_future() {
245 let expected = r"{
247 program_id: credits.aleo,
248 function_name: transfer,
249 arguments: []
250}";
251 let (remainder, candidate) =
252 Future::<CurrentNetwork>::parse("{ program_id: credits.aleo, function_name: transfer, arguments: [] }")
253 .unwrap();
254 assert!(remainder.is_empty());
255 assert_eq!(expected, candidate.to_string());
256 assert_eq!("", remainder);
257
258 let expected = r"{
260 program_id: credits.aleo,
261 function_name: transfer_public_to_private,
262 arguments: [
263 aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2,
264 100000000u64
265 ]
266}";
267 let (remainder, candidate) = Future::<CurrentNetwork>::parse(
268 "{ program_id: credits.aleo, function_name: transfer_public_to_private, arguments: [ aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2, 100000000u64 ] }",
269 ).unwrap();
270 assert!(remainder.is_empty());
271 assert_eq!(expected, candidate.to_string());
272 assert_eq!("", remainder);
273 }
274
275 #[test]
276 fn test_parse_display_roundtrip() {
277 let inner = Future::<CurrentNetwork>::new(
279 ProgramID::from_str("inner.aleo").unwrap(),
280 Identifier::from_str("bar").unwrap(),
281 vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
282 );
283
284 let expected = Future::<CurrentNetwork>::new(
285 ProgramID::from_str("test.aleo").unwrap(),
286 Identifier::from_str("foo").unwrap(),
287 vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap()), Argument::Future(inner)],
288 );
289
290 let string = expected.to_string();
292 let candidate = Future::<CurrentNetwork>::from_str(&string).unwrap();
293
294 assert_eq!(expected, candidate);
295 }
296
297 #[test]
298 fn test_parse_future_with_dynamic_future_argument() {
299 let inner = Future::<CurrentNetwork>::new(
301 ProgramID::from_str("inner.aleo").unwrap(),
302 Identifier::from_str("bar").unwrap(),
303 vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
304 );
305 let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
306
307 let expected = Future::<CurrentNetwork>::new(
309 ProgramID::from_str("test.aleo").unwrap(),
310 Identifier::from_str("foo").unwrap(),
311 vec![Argument::DynamicFuture(dynamic_inner)],
312 );
313
314 let string = expected.to_string();
316 let candidate = Future::<CurrentNetwork>::from_str(&string).unwrap();
317
318 assert_eq!(expected.program_id(), candidate.program_id());
320 assert_eq!(expected.function_name(), candidate.function_name());
321 assert_eq!(expected.arguments().len(), candidate.arguments().len());
322
323 match (&expected.arguments()[0], &candidate.arguments()[0]) {
325 (Argument::DynamicFuture(e), Argument::DynamicFuture(c)) => {
326 assert_eq!(e.program_name(), c.program_name());
327 assert_eq!(e.program_network(), c.program_network());
328 assert_eq!(e.function_name(), c.function_name());
329 assert_eq!(e.checksum(), c.checksum());
330 }
331 _ => panic!("Expected DynamicFuture argument"),
332 }
333 }
334
335 #[test]
336 fn test_deeply_nested_future() {
337 fn create_nested_future(depth: usize) -> String {
339 let root = r"{
341 program_id: foo.aleo,
342 function_name: bar,
343 arguments: []
344 }";
345 let prefix = r"{
347 program_id: foo.aleo,
348 function_name: bar,
349 arguments: ["
350 .repeat(depth);
351 let suffix = r"]}".repeat(depth);
352 format!("{prefix}{root}{suffix}")
354 }
355
356 fn run_test(depth: usize, expected_error: bool) {
358 let nested_future_string = create_nested_future(depth);
360 let result = Future::<CurrentNetwork>::parse(&nested_future_string);
362 match expected_error {
364 true => {
365 assert!(result.is_err());
366 return;
367 }
368 false => assert!(result.is_ok()),
369 };
370 let (remainder, candidate) = result.unwrap();
372 assert!(
374 remainder.is_empty(),
375 "Failed to parse deeply nested future. Found invalid character in: \"{remainder}\""
376 );
377 let expected = nested_future_string.replace("\n", "").replace(" ", "").replace("\t", "");
379 let candidate_str = candidate.to_string().replace("\n", "").replace(" ", "").replace("\t", "");
381 assert_eq!(expected, candidate_str, "Expected: {expected}, Candidate: {candidate_str}");
383 }
384
385 let mut depths = (0usize..100).collect_vec();
387 depths.extend((100..1000).step_by(100));
388 depths.extend((1000..10000).step_by(1000));
389 depths.extend((10000..100000).step_by(10000));
390
391 for depth in depths {
393 run_test(depth, depth > CurrentNetwork::MAX_DATA_DEPTH);
394 }
395 }
396}