]>
Commit | Line | Data |
---|---|---|
8cdd2311 FG |
1 | use std::collections::HashMap; |
2 | ||
3 | use anyhow::{bail, Error}; | |
4 | use rfc822_like::de::Deserializer; | |
5 | use serde::Deserialize; | |
6 | use serde_json::Value; | |
7 | ||
8 | use super::CheckSums; | |
9 | ||
10 | #[derive(Debug, Deserialize)] | |
11 | #[serde(rename_all = "PascalCase")] | |
12 | pub struct PackagesFileRaw { | |
13 | pub package: String, | |
14 | pub source: Option<String>, | |
15 | pub version: String, | |
16 | pub section: String, | |
17 | pub priority: String, | |
18 | pub architecture: String, | |
19 | pub essential: Option<String>, | |
20 | pub depends: Option<String>, | |
21 | pub recommends: Option<String>, | |
22 | pub suggests: Option<String>, | |
23 | pub breaks: Option<String>, | |
24 | pub conflicts: Option<String>, | |
25 | #[serde(rename = "Installed-Size")] | |
26 | pub installed_size: Option<String>, | |
27 | pub maintainer: String, | |
28 | pub description: String, | |
29 | pub filename: String, | |
30 | pub size: String, | |
31 | #[serde(rename = "Multi-Arch")] | |
32 | pub multi_arch: Option<String>, | |
33 | ||
34 | #[serde(rename = "MD5sum")] | |
35 | pub md5_sum: Option<String>, | |
36 | #[serde(rename = "SHA1")] | |
37 | pub sha1: Option<String>, | |
38 | #[serde(rename = "SHA256")] | |
39 | pub sha256: Option<String>, | |
40 | #[serde(rename = "SHA512")] | |
41 | pub sha512: Option<String>, | |
42 | ||
43 | #[serde(rename = "Description-md5")] | |
44 | pub description_md5: Option<String>, | |
45 | ||
46 | #[serde(flatten)] | |
47 | pub extra_fields: HashMap<String, Value>, | |
48 | } | |
49 | ||
50 | #[derive(Debug, PartialEq, Eq)] | |
51 | pub struct PackageEntry { | |
52 | pub package: String, | |
53 | pub source: Option<String>, | |
54 | pub version: String, | |
55 | pub architecture: String, | |
56 | pub file: String, | |
57 | pub size: usize, | |
58 | pub installed_size: Option<usize>, | |
59 | pub checksums: CheckSums, | |
ab6c2c74 | 60 | pub section: String, |
8cdd2311 FG |
61 | } |
62 | ||
63 | #[derive(Debug, Default, PartialEq, Eq)] | |
64 | /// A parsed representation of a Release file | |
65 | pub struct PackagesFile { | |
66 | pub files: Vec<PackageEntry>, | |
67 | } | |
68 | ||
69 | impl TryFrom<PackagesFileRaw> for PackageEntry { | |
70 | type Error = Error; | |
71 | ||
72 | fn try_from(value: PackagesFileRaw) -> Result<Self, Self::Error> { | |
73 | let installed_size = match value.installed_size { | |
74 | Some(val) => Some(val.parse::<usize>()?), | |
75 | None => None, | |
76 | }; | |
77 | ||
78 | let mut parsed = PackageEntry { | |
79 | package: value.package, | |
80 | source: value.source, | |
81 | version: value.version, | |
82 | architecture: value.architecture, | |
83 | file: value.filename, | |
84 | size: value.size.parse::<usize>()?, | |
85 | installed_size, | |
86 | checksums: CheckSums::default(), | |
ab6c2c74 | 87 | section: value.section, |
8cdd2311 FG |
88 | }; |
89 | ||
90 | if let Some(md5) = value.md5_sum { | |
91 | let mut bytes = [0u8; 16]; | |
92 | hex::decode_to_slice(md5, &mut bytes)?; | |
93 | parsed.checksums.md5 = Some(bytes); | |
94 | }; | |
95 | ||
96 | if let Some(sha1) = value.sha1 { | |
97 | let mut bytes = [0u8; 20]; | |
98 | hex::decode_to_slice(sha1, &mut bytes)?; | |
99 | parsed.checksums.sha1 = Some(bytes); | |
100 | }; | |
101 | ||
102 | if let Some(sha256) = value.sha256 { | |
103 | let mut bytes = [0u8; 32]; | |
104 | hex::decode_to_slice(sha256, &mut bytes)?; | |
105 | parsed.checksums.sha256 = Some(bytes); | |
106 | }; | |
107 | ||
108 | if let Some(sha512) = value.sha512 { | |
109 | let mut bytes = [0u8; 64]; | |
110 | hex::decode_to_slice(sha512, &mut bytes)?; | |
111 | parsed.checksums.sha512 = Some(bytes); | |
112 | }; | |
113 | ||
114 | if !parsed.checksums.is_secure() { | |
115 | bail!( | |
116 | "no strong checksum found for package entry '{}'", | |
117 | parsed.package | |
118 | ); | |
119 | } | |
120 | ||
121 | Ok(parsed) | |
122 | } | |
123 | } | |
124 | ||
125 | impl TryFrom<String> for PackagesFile { | |
126 | type Error = Error; | |
127 | ||
128 | fn try_from(value: String) -> Result<Self, Self::Error> { | |
129 | value.as_bytes().try_into() | |
130 | } | |
131 | } | |
132 | ||
133 | impl TryFrom<&[u8]> for PackagesFile { | |
134 | type Error = Error; | |
135 | ||
136 | fn try_from(value: &[u8]) -> Result<Self, Self::Error> { | |
137 | let deserialized = <Vec<PackagesFileRaw>>::deserialize(Deserializer::new(value))?; | |
138 | deserialized.try_into() | |
139 | } | |
140 | } | |
141 | ||
142 | impl TryFrom<Vec<PackagesFileRaw>> for PackagesFile { | |
143 | type Error = Error; | |
144 | ||
145 | fn try_from(value: Vec<PackagesFileRaw>) -> Result<Self, Self::Error> { | |
146 | let mut files = Vec::with_capacity(value.len()); | |
147 | for entry in value { | |
148 | let entry: PackageEntry = entry.try_into()?; | |
149 | files.push(entry); | |
150 | } | |
151 | ||
152 | Ok(Self { files }) | |
153 | } | |
154 | } | |
155 | ||
156 | #[test] | |
157 | pub fn test_deb_packages_file() { | |
158 | let input = include_str!(concat!( | |
159 | env!("CARGO_MANIFEST_DIR"), | |
160 | "/tests/deb822/packages/deb.debian.org_debian_dists_bullseye_main_binary-amd64_Packages" | |
161 | )); | |
162 | ||
163 | let deserialized = | |
164 | <Vec<PackagesFileRaw>>::deserialize(Deserializer::new(input.as_bytes())).unwrap(); | |
165 | //println!("{:?}", deserialized); | |
166 | ||
167 | let parsed: PackagesFile = deserialized.try_into().unwrap(); | |
168 | //println!("{:?}", parsed); | |
169 | ||
170 | assert_eq!(parsed.files.len(), 58618); | |
171 | } |