use crate::model::{Object, ObjectChecksums};
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum ChecksumMismatch {
Crc32c { got: u32, want: u32 },
Md5 {
got: bytes::Bytes,
want: bytes::Bytes,
},
Both {
got: Box<ObjectChecksums>,
want: Box<ObjectChecksums>,
},
}
impl std::fmt::Display for ChecksumMismatch {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Crc32c { got, want } => write!(
f,
"the CRC32C checksums do not match: got=0x{got:08x}, want=0x{want:08x}"
),
Self::Md5 { got, want } => write!(
f,
"the MD5 hashes do not match: got={:0x?}, want={:0x?}",
&got, &want
),
Self::Both { got, want } => {
write!(
f,
"both the CRC32C checksums and MD5 hashes do not match: got.crc32c=0x{:08x}, want.crc32c=0x{:08x}, got.md5={:x?}, want.md5={:x?}",
got.crc32c.unwrap_or_default(),
want.crc32c.unwrap_or_default(),
got.md5_hash,
want.md5_hash
)
}
}
}
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum KeyAes256Error {
#[error("Key has an invalid length: expected 32 bytes.")]
InvalidLength,
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum ReadError {
#[error("checksum mismatch {0}")]
ChecksumMismatch(ChecksumMismatch),
#[error("missing {0} bytes at the end of the stream")]
ShortRead(u64),
#[error("too many bytes received: expected {expected}, stopped read at {got}")]
LongRead { got: u64, expected: u64 },
#[error("unexpected success code {0} in read request, only 200 and 206 are expected")]
UnexpectedSuccessCode(u16),
#[error("the response is missing '{0}', a required header")]
MissingHeader(&'static str),
#[error("the format for header '{0}' is incorrect")]
BadHeaderFormat(
&'static str,
#[source] Box<dyn std::error::Error + Send + Sync + 'static>,
),
}
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum WriteError {
#[error(
"the service previously persisted {offset} bytes, but now reports only {persisted} as persisted"
)]
UnexpectedRewind { offset: u64, persisted: u64 },
#[error("the service reports {persisted} bytes as persisted, but we only sent {sent} bytes")]
TooMuchProgress { sent: u64, persisted: u64 },
#[error("checksum mismatch {mismatch} when uploading {} to {}", object.name, object.bucket)]
ChecksumMismatch {
mismatch: ChecksumMismatch,
object: Box<Object>,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mismatch_crc32c() {
let value = ChecksumMismatch::Crc32c {
got: 0x01020304_u32,
want: 0x02030405_u32,
};
let fmt = value.to_string();
assert!(fmt.contains("got=0x01020304"), "{value:?} => {fmt}");
assert!(fmt.contains("want=0x02030405"), "{value:?} => {fmt}");
}
#[test]
fn mismatch_md5() {
let value = ChecksumMismatch::Md5 {
got: bytes::Bytes::from_owner([0x01_u8, 0x02, 0x03, 0x04]),
want: bytes::Bytes::from_owner([0x02_u8, 0x03, 0x04, 0x05]),
};
let fmt = value.to_string();
assert!(
fmt.contains(r#"got=b"\x01\x02\x03\x04""#),
"{value:?} => {fmt}"
);
assert!(
fmt.contains(r#"want=b"\x02\x03\x04\x05""#),
"{value:?} => {fmt}"
);
}
#[test]
fn mismatch_both() {
let got = ObjectChecksums::new()
.set_crc32c(0x01020304_u32)
.set_md5_hash(bytes::Bytes::from_owner([0x01_u8, 0x02, 0x03, 0x04]));
let want = ObjectChecksums::new()
.set_crc32c(0x02030405_u32)
.set_md5_hash(bytes::Bytes::from_owner([0x02_u8, 0x03, 0x04, 0x05]));
let value = ChecksumMismatch::Both {
got: Box::new(got),
want: Box::new(want),
};
let fmt = value.to_string();
assert!(fmt.contains("got.crc32c=0x01020304"), "{value:?} => {fmt}");
assert!(fmt.contains("want.crc32c=0x02030405"), "{value:?} => {fmt}");
assert!(
fmt.contains(r#"got.md5=b"\x01\x02\x03\x04""#),
"{value:?} => {fmt}"
);
assert!(
fmt.contains(r#"want.md5=b"\x02\x03\x04\x05""#),
"{value:?} => {fmt}"
);
}
}