snarkvm_console_types_string/
parse.rs1use super::*;
17
18impl<E: Environment> Parser for StringType<E> {
19 #[inline]
21 fn parse(string: &str) -> ParserResult<Self> {
22 let (string, value) = string_parser::parse_string(string)?;
24
25 if value.len() > E::MAX_STRING_BYTES as usize {
29 return Err(Err::Error(make_error(string, ErrorKind::TooLarge)));
30 }
31
32 Ok((string, StringType::new(&value)))
33 }
34}
35
36impl<E: Environment> FromStr for StringType<E> {
37 type Err = Error;
38
39 #[inline]
41 fn from_str(string: &str) -> Result<Self> {
42 match Self::parse(string) {
43 Ok((remainder, object)) => {
44 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
46 Ok(object)
48 }
49 Err(error) => bail!("Failed to parse string. {error}"),
50 }
51 }
52}
53
54impl<E: Environment> Debug for StringType<E> {
55 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
56 Display::fmt(self, f)
57 }
58}
59
60impl<E: Environment> Display for StringType<E> {
61 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
62 write!(f, "\"{}\"", self.string)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use snarkvm_console_network_environment::Console;
70
71 type CurrentEnvironment = Console;
72
73 const ITERATIONS: u32 = 100;
74
75 #[test]
76 fn test_display() -> Result<()> {
77 assert!(StringType::<CurrentEnvironment>::parse(StringType::<CurrentEnvironment>::type_name()).is_err());
79 assert!(StringType::<CurrentEnvironment>::parse("").is_err());
80
81 assert!(StringType::<CurrentEnvironment>::parse("\"\"").is_ok());
83
84 let rng = &mut TestRng::default();
85
86 for _ in 0..ITERATIONS {
87 let expected = rng.next_string(CurrentEnvironment::MAX_STRING_BYTES / 4, false);
89 let expected_num_bytes = expected.len();
90 assert!(expected_num_bytes <= CurrentEnvironment::MAX_STRING_BYTES as usize);
91
92 let candidate = StringType::<CurrentEnvironment>::new(&expected);
93 assert_eq!(format!("\"{expected}\""), format!("{candidate}"));
94
95 let candidate_recovered = StringType::<CurrentEnvironment>::from_str(&format!("{candidate}")).unwrap();
96 assert_eq!(candidate, candidate_recovered);
97 }
98 Ok(())
99 }
100
101 #[test]
102 fn test_parse_unsupported_code_points() -> Result<()> {
103 const UNSUPPORTED_CODE_POINTS: [&str; 9] = [
104 "\u{202a}", "\u{202b}", "\u{202c}", "\u{202d}", "\u{202e}", "\u{2066}", "\u{2067}", "\u{2068}", "\u{2069}",
105 ];
106
107 for unsupported_code_point in UNSUPPORTED_CODE_POINTS {
109 assert!(StringType::<CurrentEnvironment>::parse(unsupported_code_point).is_err());
110 }
111
112 Ok(())
113 }
114
115 #[test]
116 fn test_parse_oversized_string_fails_without_halting() {
117 let max_bytes = CurrentEnvironment::MAX_STRING_BYTES as usize;
118
119 let at_capacity = format!("\"{}\"", "a".repeat(max_bytes));
121 assert!(StringType::<CurrentEnvironment>::parse(&at_capacity).is_ok());
122
123 for excess in 1..=4 {
127 let over_capacity = format!("\"{}\"", "a".repeat(max_bytes + excess));
128 assert!(StringType::<CurrentEnvironment>::parse(&over_capacity).is_err());
129 assert!(StringType::<CurrentEnvironment>::from_str(&over_capacity).is_err());
130 }
131 }
132}