snarkvm_console_types_group/
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<E: Environment> Parser for Group<E> {
19    /// Parses a string into a group circuit.
20    #[inline]
21    fn parse(string: &str) -> ParserResult<Self> {
22        // Parse the optional negative sign '-' from the string.
23        let (string, negation) = map(opt(tag("-")), |neg: Option<&str>| neg.is_some())(string)?;
24        // Parse the digits from the string.
25        let (string, primitive) = recognize(many1(terminated(one_of("0123456789"), many0(char('_')))))(string)?;
26        // Parse the group from the string.
27        let (string, group): (&str, Self) = map_res(tag(Self::type_name()), |_| {
28            let x_coordinate = primitive.replace('_', "").parse()?;
29            // Recover and negate the group element if the negative sign was present.
30            match negation {
31                true => Ok(-Group::from_x_coordinate(Field::new(x_coordinate))?),
32                false => Group::from_x_coordinate(Field::new(x_coordinate)),
33            }
34        })(string)?;
35
36        Ok((string, group))
37    }
38}
39
40impl<E: Environment> FromStr for Group<E> {
41    type Err = Error;
42
43    /// Parses a string into a group.
44    #[inline]
45    fn from_str(string: &str) -> Result<Self> {
46        match Self::parse(string) {
47            Ok((remainder, object)) => {
48                // Ensure the remainder is empty.
49                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
50                // Return the object.
51                Ok(object)
52            }
53            Err(error) => bail!("Failed to parse string. {error}"),
54        }
55    }
56}
57
58impl<E: Environment> Debug for Group<E> {
59    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
60        Display::fmt(self, f)
61    }
62}
63
64impl<E: Environment> Display for Group<E> {
65    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
66        write!(f, "{}{}", self.group.to_affine().to_x_coordinate(), Self::type_name())
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73    use snarkvm_console_network_environment::Console;
74
75    type CurrentEnvironment = Console;
76
77    const ITERATIONS: u64 = 1_000;
78
79    #[test]
80    fn test_parse() -> Result<()> {
81        let rng = &mut TestRng::default();
82
83        // Ensure empty value fails.
84        assert!(Group::<CurrentEnvironment>::parse(Group::<CurrentEnvironment>::type_name()).is_err());
85        assert!(Group::<CurrentEnvironment>::parse("").is_err());
86
87        for _ in 0..ITERATIONS {
88            // Sample a random value.
89            let group: <CurrentEnvironment as Environment>::Affine = Uniform::rand(rng);
90
91            let expected = format!("{}{}", group.to_x_coordinate(), Group::<CurrentEnvironment>::type_name());
92            let (remainder, candidate) = Group::<CurrentEnvironment>::parse(&expected).unwrap();
93            assert_eq!(format!("{expected}"), candidate.to_string());
94            assert_eq!("", remainder);
95        }
96        Ok(())
97    }
98
99    #[test]
100    fn test_display() {
101        /// Attempts to construct a group from the given element,
102        /// format it in display mode, and recover a group from it.
103        fn check_display<E: Environment>(element: E::Affine) {
104            let candidate = Group::<E>::new(element);
105            assert_eq!(format!("{}{}", element.to_x_coordinate(), Group::<E>::type_name()), format!("{candidate}"));
106
107            let candidate_recovered = Group::<E>::from_str(&format!("{candidate}")).unwrap();
108            assert_eq!(candidate, candidate_recovered);
109        }
110
111        let mut rng = TestRng::default();
112
113        for _ in 0..ITERATIONS {
114            let element = Uniform::rand(&mut rng);
115
116            check_display::<CurrentEnvironment>(element);
117        }
118    }
119
120    #[test]
121    fn test_display_zero() {
122        let zero = <CurrentEnvironment as Environment>::Affine::zero();
123
124        let candidate = Group::<CurrentEnvironment>::new(zero);
125        assert_eq!("0group", &format!("{candidate}"));
126    }
127}