1use std::{
5 hash::{Hash, Hasher},
6 io,
7 str::FromStr,
8};
9use thiserror::Error;
10
11#[derive(Debug, Error)]
12pub enum RequestError {
13 #[error("apt command failed")]
14 Command(#[from] io::Error),
15 #[error("uri not found in output: {0}")]
16 UriNotFound(String),
17 #[error("invalid URI value: {0}")]
18 UriInvalid(String),
19 #[error("name not found in output: {0}")]
20 NameNotFound(String),
21 #[error("size not found in output: {0}")]
22 SizeNotFound(String),
23 #[error("size in output could not be parsed as an integer: {0}")]
24 SizeParse(String),
25 #[error("checksum not found in output: {0}")]
26 ChecksumNotFound(String),
27 #[error("unknown checksum for print-uri output: {0}")]
28 UnknownChecksum(String),
29}
30
31#[derive(Debug, Clone, Eq, PartialEq)]
32pub enum RequestChecksum {
33 Md5(String),
34 Sha1(String),
35}
36
37#[derive(Debug, Clone, Eq)]
38pub struct Request {
39 pub uri: String,
40 pub name: String,
41 pub size: u64,
42 pub checksum: RequestChecksum,
43}
44
45impl PartialEq for Request {
46 fn eq(&self, other: &Self) -> bool {
47 self.uri == other.uri
48 }
49}
50
51impl Hash for Request {
52 fn hash<H: Hasher>(&self, state: &mut H) {
53 self.uri.hash(state);
54 }
55}
56
57impl FromStr for Request {
58 type Err = RequestError;
59
60 fn from_str(line: &str) -> Result<Self, Self::Err> {
61 let mut words = line.split_whitespace();
62
63 let mut uri = words
64 .next()
65 .ok_or_else(|| RequestError::UriNotFound(line.into()))?;
66
67 if uri.len() <= 3 {
69 return Err(RequestError::UriInvalid(uri.into()));
70 } else {
71 uri = &uri[1..uri.len() - 1];
72 }
73
74 let name = words
75 .next()
76 .ok_or_else(|| RequestError::NameNotFound(line.into()))?;
77 let size = words
78 .next()
79 .ok_or_else(|| RequestError::SizeNotFound(line.into()))?;
80 let size = size
81 .parse::<u64>()
82 .map_err(|_| RequestError::SizeParse(size.into()))?;
83
84 let checksum_string = words
85 .next()
86 .ok_or_else(|| RequestError::ChecksumNotFound(line.into()))?;
87
88 let checksum = if let Some(value) = checksum_string.strip_prefix("MD5Sum:") {
89 RequestChecksum::Md5(value.to_owned())
90 } else if let Some(value) = checksum_string.strip_prefix("SHA1:") {
91 RequestChecksum::Sha1(value.to_owned())
92 } else {
93 return Err(RequestError::UnknownChecksum(checksum_string.into()));
94 };
95
96 Ok(Request {
97 uri: uri.into(),
98 name: name.into(),
99 size,
100 checksum,
101 })
102 }
103}