Recognizes an ASCII hexadecimal [`Checksum`] from a string slice. Consumes all input. See [`Checksum::from_str`]. # Errors Returns an error if `input` is not the output of a _hash function_ in hexadecimal (or decimal in case of CRC-32/CKSUM) form.
(input: &mut &str)
| 317 | /// # Errors |
| 318 | /// |
| 319 | /// Returns an error if `input` does not start with the output of a _hash function_ |
| 320 | /// in hexadecimal (or decimal in case of CRC-32/CKSUM) form. |
| 321 | fn parser(input: &mut &str) -> ModalResult<Self> { |
| 322 | /// Consume 1 hex digit and return its hex value. |
| 323 | /// |
| 324 | /// Accepts uppercase or lowercase. |
| 325 | #[inline] |
| 326 | fn hex_digit(input: &mut &str) -> ModalResult<u8> { |
| 327 | one_of(('0'..='9', 'a'..='f', 'A'..='F')) |
| 328 | .map(|d: char| |
| 329 | // unwraps are unreachable: their invariants are always |
| 330 | // upheld because the above character set can never |
| 331 | // consume anything but a single valid hex digit |
| 332 | d.to_digit(16).unwrap().try_into().unwrap()) |
| 333 | .context(StrContext::Expected(StrContextValue::Description( |
| 334 | "ASCII hex digit", |
| 335 | ))) |
| 336 | .parse_next(input) |
| 337 | } |
| 338 | |
| 339 | let hex_pair = (hex_digit, hex_digit).map(|(first, second)| |
| 340 | // shift is infallible because hex_digit cannot return >0b00001111 |
| 341 | (first << 4) + second); |
| 342 | |
| 343 | // output size in bytes |
| 344 | let digest_bytes = <D as Digest>::output_size(); |
| 345 | |
| 346 | let digest = match D::ENCODING { |
| 347 | DigestEncoding::Hex => { |
| 348 | // Consume exactly the number of hex pairs that our Digest type expects |
| 349 | let digest = repeat(digest_bytes, hex_pair) |
| 350 | .context(StrContext::Label("hash digest")) |
| 351 | .context(StrContext::Expected(StrContextValue::Description( |
| 352 | "a hex hash digest with the appropriate length for the given algorithm.", |
| 353 | ))) |
| 354 | .parse_next(input)?; |
| 355 | |
| 356 | // Handle the case that there's another hex char after the expected number of digits |
| 357 | // This is one of the few cases that we consider a hard error. |
| 358 | cut_err(not(hex_digit)) |
| 359 | .context(StrContext::Expected(StrContextValue::Description( |
| 360 | "end of checksum (checksum is too long).", |
| 361 | ))) |
| 362 | .parse_next(input)?; |
| 363 | |
| 364 | digest |
| 365 | } |
| 366 | DigestEncoding::Dec => { |
| 367 | // output size in bits |
| 368 | let digest_bits = digest_bytes * 8; |
| 369 | |
| 370 | // The following logic parses a decimal integer for consumption by a digest. |
| 371 | // We chose to use a [`u128::MAX`] as this is the currently largest number type in |
| 372 | // the rust std library. In reality we only use this for CRC-32/CKSUM which is |
| 373 | // 4 bytes, but it's nice to keep this a bit more generic. |
| 374 | |
| 375 | // Determine the maximum allowed value based on the number of allowed |
| 376 | // `digest_bytes`. |