Skip to main content

snarkvm_console_program/data/value/
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 Value<N> {
19    /// Parses a string into a value.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        // Note that the order of the parsers matters.
23        alt((
24            map(Future::parse, Value::Future),
25            map(DynamicFuture::parse, Value::DynamicFuture),
26            map(Plaintext::parse, Value::Plaintext),
27            map(Record::parse, Value::Record),
28            map(DynamicRecord::parse, Value::DynamicRecord),
29        ))(string)
30    }
31}
32
33impl<N: Network> FromStr for Value<N> {
34    type Err = Error;
35
36    /// Parses a string into a value.
37    #[inline]
38    fn from_str(string: &str) -> Result<Self> {
39        match Self::parse(string) {
40            Ok((remainder, object)) => {
41                // Ensure the remainder is empty.
42                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
43                // Return the object.
44                Ok(object)
45            }
46            Err(error) => bail!("Failed to parse string. {error}"),
47        }
48    }
49}
50
51impl<N: Network> Debug for Value<N> {
52    /// Prints the value as a string.
53    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
54        Display::fmt(self, f)
55    }
56}
57
58impl<N: Network> Display for Value<N> {
59    /// Prints the value as a string.
60    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
61        match self {
62            Value::Plaintext(plaintext) => Display::fmt(plaintext, f),
63            Value::Record(record) => Display::fmt(record, f),
64            Value::Future(future) => Display::fmt(future, f),
65            Value::DynamicRecord(dynamic_record) => Display::fmt(dynamic_record, f),
66            Value::DynamicFuture(dynamic_future) => Display::fmt(dynamic_future, f),
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::{Argument, Entry, Identifier, Literal, Owner, ProgramID};
75    use snarkvm_console_network::MainnetV0;
76    use snarkvm_console_types::{Group, U8, U64};
77    use snarkvm_utilities::{TestRng, Uniform};
78
79    type CurrentNetwork = MainnetV0;
80
81    #[test]
82    fn test_value_plaintext_parse() {
83        // Prepare the plaintext string.
84        let string = r"{
85  owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah,
86  token_amount: 100u64
87}";
88        // Construct a new plaintext value.
89        let expected = Value::<CurrentNetwork>::from_str(string).unwrap();
90        assert!(matches!(expected, Value::Plaintext(..)));
91        assert_eq!(string, format!("{expected}"));
92    }
93
94    #[test]
95    fn test_value_record_parse() {
96        // Prepare the record string.
97        let string = r"{
98  owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.private,
99  token_amount: 100u64.private,
100  _nonce: 6122363155094913586073041054293642159180066699840940609722305038224296461351group.public,
101  _version: 0u8.public
102}";
103        // Construct a new record value.
104        let expected = Value::<CurrentNetwork>::from_str(string).unwrap();
105        assert!(matches!(expected, Value::Record(..)));
106        assert_eq!(string, format!("{expected}"));
107    }
108
109    #[test]
110    fn test_value_future_parse() {
111        // Prepare the future string.
112        let string = r"{
113  program_id: credits.aleo,
114  function_name: transfer_public_to_private,
115  arguments: [
116    aleo1g8qul5a44vk22u9uuvaewdcjw4v6xg8wx0llru39nnjn7eu08yrscxe4e2,
117    100000000u64
118  ]
119}";
120        // Construct a new future value.
121        let expected = Value::<CurrentNetwork>::from_str(string).unwrap();
122        assert!(matches!(expected, Value::Future(..)));
123        assert_eq!(string, format!("{expected}"));
124    }
125
126    #[test]
127    fn test_value_dynamic_record_parse() {
128        let rng = &mut TestRng::default();
129
130        // Create a record and convert to dynamic record.
131        let data = indexmap::indexmap! {
132            Identifier::from_str("amount").unwrap() => Entry::Private(Plaintext::from(Literal::U64(U64::rand(rng)))),
133        };
134        let owner = Owner::Public(Address::rand(rng));
135        let record = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::from_plaintext(
136            owner,
137            data,
138            Group::rand(rng),
139            U8::new(0),
140        )
141        .unwrap();
142        let expected = DynamicRecord::from_record(&record).unwrap();
143
144        // Round-trip through string parsing.
145        let string = Value::DynamicRecord(expected.clone()).to_string();
146        let Value::DynamicRecord(candidate) = Value::<CurrentNetwork>::from_str(&string).unwrap() else {
147            panic!("Expected DynamicRecord value");
148        };
149
150        // Verify the fields match.
151        assert_eq!(expected.owner(), candidate.owner());
152        assert_eq!(expected.root(), candidate.root());
153        assert_eq!(expected.nonce(), candidate.nonce());
154        assert_eq!(expected.version(), candidate.version());
155    }
156
157    #[test]
158    fn test_value_dynamic_future_parse() {
159        // Create a future and convert to dynamic future.
160        let future = Future::<CurrentNetwork>::new(
161            ProgramID::from_str("test.aleo").unwrap(),
162            Identifier::from_str("foo").unwrap(),
163            vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
164        );
165        let expected = DynamicFuture::from_future(&future).unwrap();
166
167        // Round-trip through string parsing.
168        let string = Value::DynamicFuture(expected.clone()).to_string();
169        let Value::DynamicFuture(candidate) = Value::<CurrentNetwork>::from_str(&string).unwrap() else {
170            panic!("Expected DynamicFuture value");
171        };
172
173        // Verify the fields match.
174        assert_eq!(expected.program_name(), candidate.program_name());
175        assert_eq!(expected.program_network(), candidate.program_network());
176        assert_eq!(expected.function_name(), candidate.function_name());
177        assert_eq!(expected.checksum(), candidate.checksum());
178    }
179}