1use crate::common::*;
2
3#[derive(Debug)]
4pub(crate) enum FileError {
5 Io(io::Error),
6 Missing,
7 Directory,
8 Surfeit(Bytes),
9 Dearth(Bytes),
10 Md5 {
11 expected: Md5Digest,
12 actual: Md5Digest,
13 },
14}
15
16impl FileError {
17 pub(crate) fn verify(
18 path: &Path,
19 expected_length: Bytes,
20 expected_md5: Option<Md5Digest>,
21 ) -> Result<(), FileError> {
22 let metadata = match path.metadata() {
23 Ok(metadata) => metadata,
24 Err(error) => {
25 return Err(if error.kind() == io::ErrorKind::NotFound {
26 FileError::Missing
27 } else {
28 FileError::Io(error)
29 })
30 }
31 };
32
33 if metadata.is_dir() {
34 return Err(FileError::Directory);
35 }
36
37 let actual = Bytes(metadata.len());
38
39 let difference = actual.absolute_difference(expected_length);
40
41 if actual > expected_length {
42 return Err(FileError::Surfeit(difference));
43 }
44
45 if actual < expected_length {
46 return Err(FileError::Dearth(difference));
47 }
48
49 if let Some(expected) = expected_md5 {
50 let mut reader = File::open(path)?;
51 let mut context = md5::Context::new();
52 io::copy(&mut reader, &mut context)?;
53 let actual = context.compute().into();
54
55 if actual != expected {
56 return Err(FileError::Md5 { actual, expected });
57 }
58 }
59
60 Ok(())
61 }
62}
63
64impl From<io::Error> for FileError {
65 fn from(io_error: io::Error) -> Self {
66 Self::Io(io_error)
67 }
68}
69
70impl Print for FileError {
71 fn print(&self, stream: &mut OutputStream) -> io::Result<()> {
72 let style = stream.style();
73
74 if let Self::Md5 { actual, expected } = self {
75 write!(
76 stream,
77 "MD5 checksum mismatch: {} (expected {})",
78 style.error().paint(actual.to_string()),
79 style.good().paint(expected.to_string()),
80 )?;
81
82 return Ok(());
83 }
84
85 match self {
86 Self::Io(io_error) => write!(stream, "{io_error}")?,
87 Self::Missing => write!(stream, "File missing")?,
88 Self::Directory => write!(stream, "Expected file but found directory")?,
89 Self::Surfeit(difference) => write!(stream, "{difference} too long")?,
90 Self::Dearth(difference) => write!(stream, "{difference} too short")?,
91 Self::Md5 { .. } => {
92 return Err(io::Error::new(
93 io::ErrorKind::Other,
94 Error::internal("Reached unreachable branch").to_string(),
95 ))
96 }
97 }
98
99 Ok(())
100 }
101}