apt_cmd/
request.rs

1// Copyright 2021-2022 System76 <info@system76.com>
2// SPDX-License-Identifier: MPL-2.0
3
4use 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    Sha256(String),
36    Sha512(String),
37}
38
39#[derive(Debug, Clone, Eq)]
40pub struct Request {
41    pub uri: String,
42    pub name: String,
43    pub size: u64,
44    pub checksum: RequestChecksum,
45}
46
47impl PartialEq for Request {
48    fn eq(&self, other: &Self) -> bool {
49        self.uri == other.uri
50    }
51}
52
53impl Hash for Request {
54    fn hash<H: Hasher>(&self, state: &mut H) {
55        self.uri.hash(state);
56    }
57}
58
59impl FromStr for Request {
60    type Err = RequestError;
61
62    fn from_str(line: &str) -> Result<Self, Self::Err> {
63        let mut words = line.split_whitespace();
64
65        let mut uri = words
66            .next()
67            .ok_or_else(|| RequestError::UriNotFound(line.into()))?;
68
69        // We need to remove the single quotes that apt-get encloses the URI within.
70        if uri.len() <= 3 {
71            return Err(RequestError::UriInvalid(uri.into()));
72        } else {
73            uri = &uri[1..uri.len() - 1];
74        }
75
76        let name = words
77            .next()
78            .ok_or_else(|| RequestError::NameNotFound(line.into()))?;
79        let size = words
80            .next()
81            .ok_or_else(|| RequestError::SizeNotFound(line.into()))?;
82        let size = size
83            .parse::<u64>()
84            .map_err(|_| RequestError::SizeParse(size.into()))?;
85
86        let checksum_string = words
87            .next()
88            .ok_or_else(|| RequestError::ChecksumNotFound(line.into()))?;
89
90        let checksum = if let Some(value) = checksum_string.strip_prefix("MD5Sum:") {
91            RequestChecksum::Md5(value.to_owned())
92        } else if let Some(value) = checksum_string.strip_prefix("SHA1:") {
93            RequestChecksum::Sha1(value.to_owned())
94        } else if let Some(value) = checksum_string.strip_prefix("SHA256:") {
95            RequestChecksum::Sha256(value.to_owned())
96        } else if let Some(value) = checksum_string.strip_prefix("SHA512:") {
97            RequestChecksum::Sha512(value.to_owned())
98        } else {
99            return Err(RequestError::UnknownChecksum(checksum_string.into()));
100        };
101
102        Ok(Request {
103            uri: uri.into(),
104            name: name.into(),
105            size,
106            checksum,
107        })
108    }
109}