aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack O'Connor <[email protected]>2025-02-28 11:39:42 -0800
committerJack O'Connor <[email protected]>2025-02-28 11:39:42 -0800
commit70e4ce484787d1d98d663504c97f97c13a5ea7a4 (patch)
tree1a216b766e88814a0def0402742915c94b4e292a
parent582180eb09d0369f64502fef3bc37af84c2e1fce (diff)
reduce code duplication in parse_check_line
-rw-r--r--b3sum/src/main.rs99
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,