debian_control/lossy/
apt.rs

1//! APT related structures
2use crate::lossy::Relations;
3use deb822_fast::{FromDeb822, FromDeb822Paragraph, ToDeb822, ToDeb822Paragraph};
4
5fn deserialize_components(value: &str) -> Result<Vec<String>, String> {
6    Ok(value.split_whitespace().map(|s| s.to_string()).collect())
7}
8
9fn join_whitespace(components: &[String]) -> String {
10    components.join(" ")
11}
12
13fn deserialize_architectures(value: &str) -> Result<Vec<String>, String> {
14    Ok(value.split_whitespace().map(|s| s.to_string()).collect())
15}
16
17#[derive(Debug, Clone, PartialEq, Eq, ToDeb822, FromDeb822)]
18/// A Release file
19pub struct Release {
20    #[deb822(field = "Codename")]
21    /// The codename of the release
22    pub codename: String,
23
24    #[deb822(
25        field = "Components",
26        deserialize_with = deserialize_components,
27        serialize_with = join_whitespace
28    )]
29    /// Components supported by the release
30    pub components: Vec<String>,
31
32    #[deb822(
33        field = "Architectures",
34        deserialize_with = deserialize_architectures,
35        serialize_with = join_whitespace
36    )]
37    /// Architectures supported by the release
38    pub architectures: Vec<String>,
39
40    #[deb822(field = "Description")]
41    /// Description of the release
42    pub description: String,
43
44    #[deb822(field = "Origin")]
45    /// Origin of the release
46    pub origin: String,
47
48    #[deb822(field = "Label")]
49    /// Label of the release
50    pub label: String,
51
52    #[deb822(field = "Suite")]
53    /// Suite of the release
54    pub suite: String,
55
56    #[deb822(field = "Version")]
57    /// Version of the release
58    pub version: String,
59
60    #[deb822(field = "Date")]
61    /// Date the release was published
62    pub date: String,
63
64    #[deb822(field = "NotAutomatic")]
65    /// Whether the release is not automatic
66    pub not_automatic: bool,
67
68    #[deb822(field = "ButAutomaticUpgrades")]
69    /// Indicates if packages retrieved from this release should be automatically upgraded
70    pub but_automatic_upgrades: bool,
71
72    #[deb822(field = "Acquire-By-Hash")]
73    /// Whether packages files can be acquired by hash
74    pub acquire_by_hash: bool,
75}
76
77fn deserialize_binaries(value: &str) -> Result<Vec<String>, String> {
78    Ok(value.split_whitespace().map(|s| s.to_string()).collect())
79}
80
81fn join_lines(components: &[String]) -> String {
82    components.join("\n")
83}
84
85fn deserialize_package_list(value: &str) -> Result<Vec<String>, String> {
86    Ok(value.split('\n').map(|s| s.to_string()).collect())
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, ToDeb822, FromDeb822)]
90/// A source
91pub struct Source {
92    #[deb822(field = "Directory")]
93    /// The directory of the source
94    pub directory: String,
95
96    #[deb822(field = "Description")]
97    /// Description of the source
98    pub description: Option<String>,
99
100    #[deb822(field = "Version")]
101    /// Version of the source
102    pub version: debversion::Version,
103
104    #[deb822(field = "Package")]
105    /// Package of the source
106    pub package: String,
107
108    #[deb822(field = "Binary", deserialize_with = deserialize_binaries, serialize_with = join_whitespace)]
109    /// Binaries of the source
110    pub binaries: Option<Vec<String>>,
111
112    #[deb822(field = "Maintainer")]
113    /// Maintainer of the source
114    pub maintainer: Option<String>,
115
116    #[deb822(field = "Build-Depends")]
117    /// Build dependencies of the source
118    pub build_depends: Option<String>,
119
120    #[deb822(field = "Build-Depends-Indep")]
121    /// Build dependencies independent of the architecture of the source
122    pub build_depends_indep: Option<Relations>,
123
124    #[deb822(field = "Build-Conflicts")]
125    /// Build conflicts of the source
126    pub build_conflicts: Option<Relations>,
127
128    #[deb822(field = "Build-Conflicts-Indep")]
129    /// Build conflicts independent of the architecture of the source
130    pub build_conflicts_indep: Option<Relations>,
131
132    #[deb822(field = "Standards-Version")]
133    /// Standards version of the source
134    pub standards_version: Option<String>,
135
136    #[deb822(field = "Homepage")]
137    /// Homepage of the source
138    pub homepage: Option<String>,
139
140    #[deb822(field = "Autobuild")]
141    /// Whether the source should be autobuilt
142    pub autobuild: Option<bool>,
143
144    #[deb822(field = "Testsuite")]
145    /// Testsuite of the source
146    pub testsuite: Option<String>,
147
148    #[deb822(field = "Vcs-Browser")]
149    /// VCS browser of the source
150    pub vcs_browser: Option<String>,
151
152    #[deb822(field = "Vcs-Git")]
153    /// VCS Git of the source
154    pub vcs_git: Option<String>,
155
156    #[deb822(field = "Vcs-Bzr")]
157    /// VCS Bzr of the source
158    pub vcs_bzr: Option<String>,
159
160    #[deb822(field = "Vcs-Hg")]
161    /// VCS Hg of the source
162    pub vcs_hg: Option<String>,
163
164    #[deb822(field = "Vcs-Svn")]
165    /// VCS SVN of the source
166    pub vcs_svn: Option<String>,
167
168    #[deb822(field = "Vcs-Darcs")]
169    /// VCS Darcs of the source
170    pub vcs_darcs: Option<String>,
171
172    #[deb822(field = "Vcs-Cvs")]
173    /// VCS CVS of the source
174    pub vcs_cvs: Option<String>,
175
176    #[deb822(field = "Vcs-Arch")]
177    /// VCS Arch of the source
178    pub vcs_arch: Option<String>,
179
180    #[deb822(field = "Vcs-Mtn")]
181    /// VCS Mtn of the source
182    pub vcs_mtn: Option<String>,
183
184    #[deb822(field = "Priority")]
185    /// Priority of the source
186    pub priority: Option<crate::fields::Priority>,
187
188    #[deb822(field = "Section")]
189    /// Section of the source
190    pub section: Option<String>,
191
192    #[deb822(field = "Format")]
193    /// Format of the source
194    pub format: Option<String>,
195
196    #[deb822(field = "Package-List", deserialize_with = deserialize_package_list, serialize_with = join_lines)]
197    /// Package list of the source
198    pub package_list: Vec<String>,
199}
200
201impl std::str::FromStr for Source {
202    type Err = String;
203
204    fn from_str(s: &str) -> Result<Self, Self::Err> {
205        let para = s
206            .parse::<deb822_fast::Paragraph>()
207            .map_err(|e| e.to_string())?;
208
209        FromDeb822Paragraph::from_paragraph(&para)
210    }
211}
212
213impl std::fmt::Display for Source {
214    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
215        let para: deb822_fast::Paragraph = self.to_paragraph();
216        write!(f, "{}", para)
217    }
218}
219
220/// A package
221#[derive(Debug, Clone, PartialEq, Eq, ToDeb822, FromDeb822)]
222pub struct Package {
223    /// The name of the package
224    #[deb822(field = "Package")]
225    pub name: String,
226
227    /// The version of the package
228    #[deb822(field = "Version")]
229    pub version: debversion::Version,
230
231    /// The architecture of the package
232    #[deb822(field = "Architecture")]
233    pub architecture: String,
234
235    /// The maintainer of the package
236    #[deb822(field = "Maintainer")]
237    pub maintainer: Option<String>,
238
239    /// The installed size of the package
240    #[deb822(field = "Installed-Size")]
241    pub installed_size: Option<usize>,
242
243    /// Dependencies
244    #[deb822(field = "Depends")]
245    pub depends: Option<Relations>,
246
247    /// Pre-Depends
248    #[deb822(field = "Pre-Depends")]
249    pub pre_depends: Option<Relations>,
250
251    /// Recommends
252    #[deb822(field = "Recommends")]
253    pub recommends: Option<Relations>,
254
255    /// Suggests
256    #[deb822(field = "Suggests")]
257    pub suggests: Option<Relations>,
258
259    /// Enhances
260    #[deb822(field = "Enhances")]
261    pub enhances: Option<Relations>,
262
263    /// Breaks
264    #[deb822(field = "Breaks")]
265    pub breaks: Option<Relations>,
266
267    /// Conflicts
268    #[deb822(field = "Conflicts")]
269    pub conflicts: Option<Relations>,
270
271    /// Provides
272    #[deb822(field = "Provides")]
273    pub provides: Option<Relations>,
274
275    /// Replaces
276    #[deb822(field = "Replaces")]
277    pub replaces: Option<Relations>,
278
279    /// Built-Using
280    #[deb822(field = "Built-Using")]
281    pub built_using: Option<Relations>,
282
283    /// Description
284    #[deb822(field = "Description")]
285    pub description: Option<String>,
286
287    /// Homepage
288    #[deb822(field = "Homepage")]
289    pub homepage: Option<String>,
290
291    /// Priority
292    #[deb822(field = "Priority")]
293    pub priority: Option<crate::fields::Priority>,
294
295    /// Section
296    #[deb822(field = "Section")]
297    pub section: Option<String>,
298
299    /// Essential
300    #[deb822(field = "Essential")]
301    pub essential: Option<bool>,
302
303    /// Tag
304    #[deb822(field = "Tag")]
305    pub tag: Option<String>,
306
307    /// Size
308    #[deb822(field = "Size")]
309    pub size: Option<usize>,
310
311    /// MD5sum
312    #[deb822(field = "MD5sum")]
313    pub md5sum: Option<String>,
314
315    /// SHA256
316    #[deb822(field = "SHA256")]
317    pub sha256: Option<String>,
318
319    /// Description (MD5)
320    #[deb822(field = "Description-MD5")]
321    pub description_md5: Option<String>,
322}
323
324impl std::str::FromStr for Package {
325    type Err = String;
326
327    fn from_str(s: &str) -> Result<Self, Self::Err> {
328        let para = s
329            .parse::<deb822_fast::Paragraph>()
330            .map_err(|e| e.to_string())?;
331
332        FromDeb822Paragraph::from_paragraph(&para)
333    }
334}
335
336impl std::fmt::Display for Package {
337    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
338        let para: deb822_fast::Paragraph = self.to_paragraph();
339        write!(f, "{}", para)
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346    use deb822_fast::Paragraph;
347    use deb822_fast::ToDeb822Paragraph;
348
349    #[test]
350    fn test_release() {
351        let release = Release {
352            codename: "focal".to_string(),
353            components: vec!["main".to_string(), "restricted".to_string()],
354            architectures: vec!["amd64".to_string(), "arm64".to_string()],
355            description: "Ubuntu 20.04 LTS".to_string(),
356            origin: "Ubuntu".to_string(),
357            label: "Ubuntu".to_string(),
358            suite: "focal".to_string(),
359            version: "20.04".to_string(),
360            date: "Thu, 23 Apr 2020 17:19:19 UTC".to_string(),
361            not_automatic: false,
362            but_automatic_upgrades: true,
363            acquire_by_hash: true,
364        };
365
366        let deb822 = r#"Codename: focal
367Components: main restricted
368Architectures: amd64 arm64
369Description: Ubuntu 20.04 LTS
370Origin: Ubuntu
371Label: Ubuntu
372Suite: focal
373Version: 20.04
374Date: Thu, 23 Apr 2020 17:19:19 UTC
375NotAutomatic: false
376ButAutomaticUpgrades: true
377Acquire-By-Hash: true
378"#;
379
380        let para = deb822.parse::<Paragraph>().unwrap();
381
382        let release: deb822_fast::Paragraph = release.to_paragraph();
383
384        assert_eq!(release, para);
385    }
386
387    #[test]
388    fn test_package() {
389        let package = r#"Package: apt
390Version: 2.1.10
391Architecture: amd64
392Maintainer: APT Development Team <apt@lists.debian.org>
393Installed-Size: 3524
394Depends: libc6 (>= 2.14), libgcc1
395Pre-Depends: dpkg (>= 1.15.6)
396Recommends: gnupg
397Suggests: apt-doc, aptitude | synaptic | wajig
398"#;
399
400        let package: Package = package.parse().unwrap();
401
402        assert_eq!(package.name, "apt");
403        assert_eq!(package.version, "2.1.10".parse().unwrap());
404        assert_eq!(package.architecture, "amd64");
405    }
406}