diff options
| author | Jack O'Connor <[email protected]> | 2025-02-28 11:39:42 -0800 |
|---|---|---|
| committer | Jack O'Connor <[email protected]> | 2025-02-28 11:39:42 -0800 |
| commit | 70e4ce484787d1d98d663504c97f97c13a5ea7a4 (patch) | |
| tree | 1a216b766e88814a0def0402742915c94b4e292a /b3sum | |
| parent | 582180eb09d0369f64502fef3bc37af84c2e1fce (diff) | |
reduce code duplication in parse_check_line
Diffstat (limited to 'b3sum')
| -rw-r--r-- | b3sum/src/main.rs | 99 |
1 files changed, 42 insertions, 57 deletions
diff --git a/b3sum/src/main.rs b/b3sum/src/main.rs index 8d63688..ba9db13 100644 --- a/b3sum/src/main.rs +++ b/b3sum/src/main.rs @@ -334,6 +334,18 @@ struct ParsedCheckLine { expected_hash: blake3::Hash, } +fn split_untagged_check_line(line_after_slash: &str) -> Option<(&str, &str)> { + line_after_slash.split_once(" ") +} + +fn split_tagged_check_line(line_after_slash: &str) -> Option<(&str, &str)> { + let prefix = "BLAKE3 ("; + if !line_after_slash.starts_with(prefix) { + return None; + } + line_after_slash[prefix.len()..].split_once(") = ") +} + fn parse_check_line(mut line: &str) -> Result<ParsedCheckLine> { // Trim off the trailing newlines, if any. line = line.trim_end_matches(['\r', '\n']); @@ -342,79 +354,52 @@ fn parse_check_line(mut line: &str) -> Result<ParsedCheckLine> { let Some(first) = line.chars().next() else { bail!("Empty line"); }; - let mut is_escaped = false; + let line_after_slash; + let is_escaped; if first == '\\' { is_escaped = true; - line = &line[1..]; - } - - if line.starts_with("BLAKE3 (") { - match line.rsplit_once(')') { - Some((file, hash_hex)) => { - ensure!(hash_hex.starts_with(" = "), "Invalid tagged line"); - let file_string = file[8..].to_string(); - let file_path_string = if is_escaped { - // If we detected a backslash at the start of the line earlier, now we - // need to unescape backslashes and newlines. - unescape(&file_string)? - } else { - file_string.clone().into() - }; - // Decode the hash hex. - let mut hash_bytes = [0; blake3::OUT_LEN]; - let mut hex_chars = hash_hex[3..].chars(); - let hash_hex_len = 2 * blake3::OUT_LEN; - ensure!(hash_hex.len() - 3 == hash_hex_len, "Wrong hash length"); - for byte in &mut hash_bytes { - let high_char = hex_chars.next().unwrap(); - let low_char = hex_chars.next().unwrap(); - *byte = 16 * hex_half_byte(high_char)? + hex_half_byte(low_char)?; - } - let expected_hash: blake3::Hash = hash_bytes.into(); - check_for_invalid_characters(&file_path_string)?; - return Ok(ParsedCheckLine { - file_string, - is_escaped, - file_path: file_path_string.into(), - expected_hash, - }); - } - None => ensure!(false, "Invalid tagged line"), - } + line_after_slash = &line[1..]; + } else { + is_escaped = false; + line_after_slash = line; + } + + // Split the line. It might be "<hash> <file>" or "BLAKE3(<file>) = <hash>". The latter comes + // from the --tag flag. + let hash_hex; + let file_str; + if let Some((left, right)) = split_untagged_check_line(line_after_slash) { + hash_hex = left; + file_str = right; + } else if let Some((left, right)) = split_tagged_check_line(line_after_slash) { + file_str = left; + hash_hex = right; + } else { + bail!("Invalid check line format"); } - // The front of the line must be a hash of the usual length, followed by - // two spaces. The hex characters in the hash must be lowercase for now, - // though we could support uppercase too if we wanted to. - let hash_hex_len = 2 * blake3::OUT_LEN; - let num_spaces = 2; - let prefix_len = hash_hex_len + num_spaces; - ensure!(line.len() > prefix_len, "Short line"); - ensure!( - line.chars().take(prefix_len).all(|c| c.is_ascii()), - "Non-ASCII prefix" - ); - ensure!(&line[hash_hex_len..][..2] == " ", "Invalid space"); - // Decode the hash hex. + // Decode the hex hash. + ensure!(hash_hex.len() == 2 * blake3::OUT_LEN, "Invalid hash length"); + let mut hex_chars = hash_hex.chars(); let mut hash_bytes = [0; blake3::OUT_LEN]; - let mut hex_chars = line[..hash_hex_len].chars(); for byte in &mut hash_bytes { let high_char = hex_chars.next().unwrap(); let low_char = hex_chars.next().unwrap(); *byte = 16 * hex_half_byte(high_char)? + hex_half_byte(low_char)?; } let expected_hash: blake3::Hash = hash_bytes.into(); - let file_string = line[prefix_len..].to_string(); + + // Unescape and validate the filepath. let file_path_string = if is_escaped { - // If we detected a backslash at the start of the line earlier, now we - // need to unescape backslashes and newlines. - unescape(&file_string)? + unescape(file_str)? } else { - file_string.clone().into() + file_str.to_string() }; + ensure!(!file_path_string.is_empty(), "empty file path"); check_for_invalid_characters(&file_path_string)?; + Ok(ParsedCheckLine { - file_string, + file_string: file_str.to_string(), is_escaped, file_path: file_path_string.into(), expected_hash, |
