parse_plaintext.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the alphavm 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 16 use super::*; 17 18 impl<N: Network> Parser for Record<N, Plaintext<N>> { 19 /// Parses a string as a record: `{ owner: address, identifier_0: entry_0, ..., identifier_n: entry_n, _nonce: field, _version: u8 }`. 20 #[inline] 21 fn parse(string: &str) -> ParserResult<Self> { 22 /// Parses a sanitized pair: `identifier: entry`. 23 #[allow(clippy::type_complexity)] 24 fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<N>, Entry<N, Plaintext<N>>)> { 25 // Parse the whitespace and comments from the string. 26 let (string, _) = Sanitizer::parse(string)?; 27 // Parse the identifier from the string. 28 let (string, identifier) = Identifier::parse(string)?; 29 // Parse the whitespace from the string. 30 let (string, _) = Sanitizer::parse_whitespaces(string)?; 31 // Parse the ":" from the string. 32 let (string, _) = tag(":")(string)?; 33 // Parse the whitespace and comments from the string. 34 let (string, _) = Sanitizer::parse(string)?; 35 // Parse the entry from the string. 36 let (string, entry) = Entry::parse(string)?; 37 // Parse the whitespace from the string. 38 let (string, _) = Sanitizer::parse_whitespaces(string)?; 39 // Return the identifier and entry. 40 Ok((string, (identifier, entry))) 41 } 42 43 // Parse the whitespace and comments from the string. 44 let (string, _) = Sanitizer::parse(string)?; 45 // Parse the "{" from the string. 46 let (string, _) = tag("{")(string)?; 47 48 // Parse the whitespace and comments from the string. 49 let (string, _) = Sanitizer::parse(string)?; 50 // Parse the "owner" tag from the string. 51 let (string, _) = tag("owner")(string)?; 52 // Parse the whitespace from the string. 53 let (string, _) = Sanitizer::parse_whitespaces(string)?; 54 // Parse the ":" from the string. 55 let (string, _) = tag(":")(string)?; 56 // Parse the whitespace and comments from the string. 57 let (string, _) = Sanitizer::parse(string)?; 58 // Parse the owner from the string. 59 let (string, owner) = alt(( 60 map(pair(Address::parse, tag(".public")), |(owner, _)| Owner::Public(owner)), 61 map(pair(Address::parse, tag(".private")), |(owner, _)| { 62 Owner::Private(Plaintext::from(Literal::Address(owner))) 63 }), 64 ))(string)?; 65 // Parse the "," from the string. 66 let (string, _) = tag(",")(string)?; 67 68 // Parse the entries. 69 let (string, entries) = map_res(separated_list0(tag(","), parse_pair), |entries: Vec<_>| { 70 // Prepare the reserved entry names. 71 let reserved = [Identifier::from_str("owner").map_err(|e| error(e.to_string()))?]; 72 // Ensure the entries has no duplicate names. 73 if has_duplicates(entries.iter().map(|(identifier, _)| identifier).chain(reserved.iter())) { 74 return Err(error("Duplicate entry type found in record")); 75 } 76 // Ensure the number of entries is within the maximum limit. 77 match entries.len() <= N::MAX_DATA_ENTRIES { 78 true => Ok(entries), 79 false => Err(error(format!("Found a record that exceeds size ({})", entries.len()))), 80 } 81 })(string)?; 82 83 // If there are entries, then parse the "," from the string. 84 let string = match !entries.is_empty() { 85 // Parse the "," from the string. 86 true => tag(",")(string)?.0, 87 false => string, 88 }; 89 90 // Parse the whitespace and comments from the string. 91 let (string, _) = Sanitizer::parse(string)?; 92 // Parse the "_nonce" tag from the string. 93 let (string, _) = tag("_nonce")(string)?; 94 // Parse the ":" from the string. 95 let (string, _) = tag(":")(string)?; 96 // Parse the whitespace and comments from the string. 97 let (string, _) = Sanitizer::parse(string)?; 98 // Parse the nonce from the string. 99 let (string, (nonce, _)) = pair(Group::parse, tag(".public"))(string)?; 100 101 // There may be an optional "_version" tag. Parse the "," from the string if it exists. 102 let string = match opt(tag(","))(string)? { 103 // If there is a version, then parse the "," from the string. 104 (string, Some(_)) => string, 105 // If there is no version, then keep the string as is. 106 (string, None) => string, 107 }; 108 109 // Parse the whitespace and comments from the string. 110 let (string, _) = Sanitizer::parse(string)?; 111 // Parse the optional "_version" tag from the string. 112 let (string, version) = match opt(tag("_version"))(string)? { 113 // If there is no version, then set the version to zero. 114 (string, None) => (string, U8::zero()), 115 // If there is a version, then parse the version from the string. 116 (string, Some(_)) => { 117 // Parse the whitespace and comments from the string. 118 let (string, _) = Sanitizer::parse(string)?; 119 // Parse the ":" from the string. 120 let (string, _) = tag(":")(string)?; 121 // Parse the whitespace and comments from the string. 122 let (string, _) = Sanitizer::parse(string)?; 123 // Parse the version from the string. 124 terminated(U8::parse, tag(".public"))(string)? 125 } 126 }; 127 128 // Parse the whitespace and comments from the string. 129 let (string, _) = Sanitizer::parse(string)?; 130 // Parse the '}' from the string. 131 let (string, _) = tag("}")(string)?; 132 // Output the record. 133 Ok((string, Record { owner, data: IndexMap::from_iter(entries), nonce, version })) 134 } 135 } 136 137 impl<N: Network> FromStr for Record<N, Plaintext<N>> { 138 type Err = Error; 139 140 /// Returns a record from a string literal. 141 fn from_str(string: &str) -> Result<Self> { 142 match Self::parse(string) { 143 Ok((remainder, object)) => { 144 // Ensure the remainder is empty. 145 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\""); 146 // Return the object. 147 Ok(object) 148 } 149 Err(error) => bail!("Failed to parse string. {error}"), 150 } 151 } 152 } 153 154 impl<N: Network> Debug for Record<N, Plaintext<N>> { 155 /// Prints the record as a string. 156 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 157 Display::fmt(self, f) 158 } 159 } 160 161 impl<N: Network> Display for Record<N, Plaintext<N>> { 162 /// Prints the record as a string. 163 fn fmt(&self, f: &mut Formatter) -> fmt::Result { 164 self.fmt_internal(f, 0) 165 } 166 } 167 168 impl<N: Network> Record<N, Plaintext<N>> { 169 /// Prints the record with the given indentation depth. 170 fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result { 171 /// The number of spaces to indent. 172 const INDENT: usize = 2; 173 174 // Print the opening brace. 175 write!(f, "{{")?; 176 // Print the owner with a comma. 177 write!(f, "\n{:indent$}owner: {},", "", self.owner, indent = (depth + 1) * INDENT)?; 178 // Print the data with a comma. 179 for (identifier, entry) in self.data.iter() { 180 // Print the identifier. 181 write!(f, "\n{:indent$}{identifier}: ", "", indent = (depth + 1) * INDENT)?; 182 // Print the entry. 183 match entry { 184 // If the entry is a literal, print the entry without indentation. 185 Entry::Constant(Plaintext::Literal(..)) 186 | Entry::Public(Plaintext::Literal(..)) 187 | Entry::Private(Plaintext::Literal(..)) => write!(f, "{entry}")?, 188 // If the entry is a struct or an array, print the entry with indentation. 189 Entry::Constant(Plaintext::Struct(..)) 190 | Entry::Public(Plaintext::Struct(..)) 191 | Entry::Private(Plaintext::Struct(..)) 192 | Entry::Constant(Plaintext::Array(..)) 193 | Entry::Public(Plaintext::Array(..)) 194 | Entry::Private(Plaintext::Array(..)) => entry.fmt_internal(f, depth + 1)?, 195 } 196 // Print the comma. 197 write!(f, ",")?; 198 } 199 // Print the nonce with a comma. 200 write!(f, "\n{:indent$}_nonce: {}.public,", "", self.nonce, indent = (depth + 1) * INDENT)?; 201 // Print the version without a comma. 202 write!(f, "\n{:indent$}_version: {}.public", "", self.version, indent = (depth + 1) * INDENT)?; 203 // Print the closing brace. 204 write!(f, "\n{:indent$}}}", "", indent = depth * INDENT) 205 } 206 } 207 208 #[cfg(test)] 209 mod tests { 210 use super::*; 211 use alphavm_console_network::MainnetV0; 212 213 type CurrentNetwork = MainnetV0; 214 215 #[test] 216 fn test_parse_without_version() -> Result<()> { 217 // Sanity check. 218 let expected = r"{ 219 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 220 _nonce: 0group.public, 221 _version: 0u8.public 222 }"; 223 let given = 224 "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public }"; 225 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 226 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 227 assert_eq!(expected, candidate.to_string()); 228 assert_eq!("", remainder); 229 Ok(()) 230 } 231 232 #[test] 233 fn test_parse_with_version() -> Result<()> { 234 // Sanity check. 235 let expected = r"{ 236 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 237 _nonce: 0group.public, 238 _version: 0u8.public 239 }"; 240 let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public, _version: 0u8.public }"; 241 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 242 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 243 assert_eq!(expected, candidate.to_string()); 244 assert_eq!("", remainder); 245 246 // Sanity check. 247 let expected = r"{ 248 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 249 _nonce: 0group.public, 250 _version: 1u8.public 251 }"; 252 let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public, _version: 1u8.public }"; 253 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 254 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 255 assert_eq!(expected, candidate.to_string()); 256 assert_eq!("", remainder); 257 Ok(()) 258 } 259 260 #[test] 261 fn test_parse_without_data_entries() -> Result<()> { 262 // Sanity check. 263 let expected = r"{ 264 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 265 _nonce: 0group.public, 266 _version: 0u8.public 267 }"; 268 let given = 269 "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, _nonce: 0group.public }"; 270 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 271 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 272 assert_eq!(expected, candidate.to_string()); 273 assert_eq!("", remainder); 274 Ok(()) 275 } 276 277 #[test] 278 fn test_parse_with_literal_entry() -> Result<()> { 279 let expected = r"{ 280 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, 281 foo: 5u8.constant, 282 _nonce: 0group.public, 283 _version: 0u8.public 284 }"; 285 let given = "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, foo: 5u8.constant, _nonce: 0group.public }"; 286 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?; 287 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 288 assert_eq!(expected, candidate.to_string()); 289 assert_eq!("", remainder); 290 Ok(()) 291 } 292 293 #[test] 294 fn test_parse_with_struct_entry() -> Result<()> { 295 let expected = r"{ 296 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, 297 foo: 5u8.public, 298 bar: { 299 baz: 6u8.constant, 300 qux: 7u8.constant 301 }, 302 quux: 8u8.private, 303 corge: { 304 grault: 9u8.constant, 305 garply: { 306 waldo: 10u8.constant, 307 fred: 11u8.constant 308 } 309 }, 310 xyzzy: { 311 thud: 12u8.public 312 }, 313 _nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public, 314 _version: 0u8.public 315 }"; 316 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 317 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 318 assert_eq!(expected, candidate.to_string()); 319 assert_eq!("", remainder); 320 321 let expected = r"{ 322 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 323 foo: { 324 bar: 0u128.private 325 }, 326 baz: { 327 quine: { 328 flop: 0u64.private 329 }, 330 slice: 0u16.private, 331 flag: true.private, 332 square: { 333 first: 0u128.private, 334 second: 1u128.private, 335 third: 2u128.private, 336 fourth: 3u128.private 337 } 338 }, 339 _nonce: 0group.public, 340 _version: 0u8.public 341 }"; 342 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 343 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 344 assert_eq!(expected, candidate.to_string()); 345 assert_eq!("", remainder); 346 347 let expected = r"{ 348 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 349 c: { 350 c: { 351 a: 0u8.private, 352 b: 1u8.private 353 }, 354 d: { 355 a: 0u8.private, 356 b: 1u8.private 357 } 358 }, 359 d: { 360 c: { 361 a: 0u8.private, 362 b: 1u8.private 363 }, 364 d: { 365 a: 0u8.private, 366 b: 1u8.private 367 } 368 }, 369 _nonce: 8102307625287186026775464343238779600702564007094834161216556016558567413871group.public, 370 _version: 0u8.public 371 }"; 372 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 373 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 374 assert_eq!(expected, candidate.to_string()); 375 assert_eq!("", remainder); 376 377 Ok(()) 378 } 379 380 #[test] 381 fn test_parse_with_array_entry() -> Result<()> { 382 let expected = r"{ 383 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, 384 foo: 5u8.public, 385 bar: [ 386 6u8.private, 387 7u8.private, 388 8u8.private 389 ], 390 _nonce: 0group.public, 391 _version: 0u8.public 392 }"; 393 let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?; 394 println!("\nExpected: {expected}\n\nFound: {candidate}\n"); 395 assert_eq!(expected, candidate.to_string()); 396 assert_eq!("", remainder); 397 Ok(()) 398 } 399 400 #[test] 401 fn test_parse_fails() -> Result<()> { 402 // Missing owner. 403 let expected = "{ foo: 5u8.private, _nonce: 0group.public }"; 404 assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err()); 405 406 // Missing nonce. 407 let expected = 408 "{ owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.public, foo: 5u8.private }"; 409 assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err()); 410 411 // Entry 'd' contains members with different visibility. 412 let expected = r"{ 413 owner: ax150w2lvhdzychwvzu54ys5zas7tm5s0ycdyw563pms83g9u0vucgqe5fs5w.private, 414 a: true.private, 415 b: 123456789field.private, 416 c: 0group.private, 417 d: { 418 e: true.private, 419 f: 123456789field.public, 420 g: 0group.private 421 }, 422 _nonce: 0group.public, 423 _version: 0u8.public 424 }"; 425 assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err()); 426 Ok(()) 427 } 428 }