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}
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        // We need to remove the single quotes that apt-get encloses the URI within.
68        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}