apt_release_file/
entry.rs1use super::ImageSize;
2use deb_architectures::Architecture;
3use std::str::FromStr;
4
5#[derive(Debug, Default, Clone, Hash, PartialEq)]
7pub struct ReleaseEntry {
8 pub sum: String,
9 pub size: u64,
10 pub path: String,
11}
12
13impl ReleaseEntry {
14 pub fn variant(&self) -> Option<EntryVariant> {
18 entry_variant(&self.path)
19 }
20}
21
22impl FromStr for ReleaseEntry {
23 type Err = &'static str;
24
25 fn from_str(input: &str) -> Result<Self, Self::Err> {
26 let mut iterator = input.split_whitespace();
27
28 let output = Self {
29 sum: iterator.next().ok_or("missing sum field")?.to_owned(),
30 size: iterator
31 .next()
32 .ok_or("missing size field")?
33 .parse::<u64>()
34 .map_err(|_| "size field is not a number")?,
35 path: iterator.next().ok_or("missing path field")?.to_owned(),
36 };
37
38 Ok(output)
39 }
40}
41
42#[derive(Debug, Clone, Hash, PartialEq)]
44pub enum EntryVariant {
45 Binary(BinaryEntry, Architecture),
46 Contents(Architecture, Option<String>),
47 Dep11(Dep11Entry),
48 Source(SourceEntry),
49 I18n(I18nEntry),
50}
51
52#[derive(Debug, Clone, Hash, PartialEq)]
54pub enum Dep11Entry {
55 Components(Architecture, Option<String>),
56 Icons(ImageSize, Option<String>),
57}
58
59#[derive(Debug, Clone, Hash, PartialEq)]
61pub enum I18nEntry {
62 Index,
63 Translations(String, Option<String>),
64}
65
66#[derive(Debug, Clone, Hash, PartialEq)]
68pub enum BinaryEntry {
69 Packages(Option<String>),
70 Release,
71}
72
73#[derive(Debug, Clone, Hash, PartialEq)]
75pub enum SourceEntry {
76 Sources(Option<String>),
77 Release,
78}
79
80fn extension_from(input: &str, len: usize) -> Option<String> {
82 if input.len() < len + 1 {
83 None
84 } else {
85 Some(input[len + 1..].to_owned())
86 }
87}
88
89fn type_with_extension<T: FromStr>(input: &str) -> Option<(T, Option<String>)> {
91 let (kind, ext) = match input.find('.') {
92 Some(pos) => (&input[..pos], Some(input[pos + 1..].to_owned())),
93 None => (input, None),
94 };
95
96 kind.parse::<T>().ok().map(|kind| (kind, ext))
97}
98
99fn entry_variant(original_path: &str) -> Option<EntryVariant> {
100 let mut path = original_path;
101 let mut found = false;
102 while let Some(pos) = path.find('/') {
103 found = true;
104 let base = &path[..pos];
105
106 match base {
107 _ if base.starts_with("binary-") => {
108 let binary = &path[7..];
109
110 return binary.find('/').and_then(|pos| {
111 binary[..pos].parse::<Architecture>().ok().and_then(|arch| {
112 let filename = &binary[pos + 1..];
113 if filename.starts_with("Packages") {
114 let ext = extension_from(filename, 8);
115 Some(EntryVariant::Binary(BinaryEntry::Packages(ext), arch))
116 } else if filename.starts_with("Release") {
117 Some(EntryVariant::Binary(BinaryEntry::Release, arch))
118 } else {
119 None
120 }
121 })
122 });
123 }
124 "debian-installer" => {
125 return None;
126 }
128 "dep11" => {
129 let path = &path[6..];
130 return if path.starts_with("icons-") {
131 type_with_extension::<ImageSize>(&path[6..])
132 .map(|(res, ext)| EntryVariant::Dep11(Dep11Entry::Icons(res, ext)))
133 } else if path.starts_with("Components-") {
134 type_with_extension::<Architecture>(&path[11..])
135 .map(|(arch, ext)| EntryVariant::Dep11(Dep11Entry::Components(arch, ext)))
136 } else {
137 None
138 };
139 }
140 "i18n" => {
141 let path = &path[5..];
142 return if path.starts_with("Translation") {
143 type_with_extension::<String>(&path[12..])
144 .map(|(loc, ext)| EntryVariant::I18n(I18nEntry::Translations(loc, ext)))
145 } else if path == "Index" {
146 Some(EntryVariant::I18n(I18nEntry::Index))
147 } else {
148 None
149 };
150 }
151 "source" => {
152 let path = &path[7..];
153 return if path.starts_with("Sources") {
154 let ext = extension_from(path, 7);
155 Some(EntryVariant::Source(SourceEntry::Sources(ext)))
156 } else if path == "Release" {
157 Some(EntryVariant::Source(SourceEntry::Release))
158 } else {
159 None
160 };
161 }
162 _ => path = &path[pos + 1..],
163 }
164 }
165
166 if !found && original_path.starts_with("Contents-") {
167 return type_with_extension::<Architecture>(&original_path[9..])
168 .map(|(arch, ext)| EntryVariant::Contents(arch, ext));
169 }
170
171 None
172}
173
174#[cfg(test)]
175mod tests {
176 use super::*;
177
178 #[test]
179 fn entry_parsing() {
180 assert_eq!(
181 entry_variant("binary-amd64/Packages.xz").expect("bad entry result"),
182 EntryVariant::Binary(
183 BinaryEntry::Packages(Some("xz".into())),
184 Architecture::Amd64
185 )
186 )
187 }
188}