debian_control/lossless/
apt.rs

1//! APT package manager files
2use crate::fields::{
3    Md5Checksum, MultiArch, Priority, Sha1Checksum, Sha256Checksum, Sha512Checksum,
4};
5use crate::lossless::relations::Relations;
6
7/// A source package in the APT package manager.
8pub struct Source(deb822_lossless::Paragraph);
9
10#[cfg(feature = "python-debian")]
11impl pyo3::ToPyObject for Source {
12    fn to_object(&self, py: pyo3::Python) -> pyo3::PyObject {
13        use pyo3::prelude::*;
14        let d = self.0.to_object(py);
15
16        let m = py.import_bound("debian.deb822").unwrap();
17        let cls = m.getattr("Sources").unwrap();
18
19        cls.call1((d,)).unwrap().to_object(py)
20    }
21}
22
23#[cfg(feature = "python-debian")]
24impl pyo3::FromPyObject<'_> for Source {
25    fn extract_bound(ob: &pyo3::Bound<pyo3::PyAny>) -> pyo3::PyResult<Self> {
26        use pyo3::prelude::*;
27        Ok(Source(ob.extract()?))
28    }
29}
30
31impl From<deb822_lossless::Paragraph> for Source {
32    fn from(paragraph: deb822_lossless::Paragraph) -> Self {
33        Self(paragraph)
34    }
35}
36
37impl Default for Source {
38    fn default() -> Self {
39        Self(deb822_lossless::Paragraph::new())
40    }
41}
42
43impl Source {
44    /// Create a new source package
45    pub fn new() -> Self {
46        Self(deb822_lossless::Paragraph::new())
47    }
48
49    /// Get the source name
50    pub fn package(&self) -> Option<String> {
51        self.0.get("Package").map(|s| s.to_string())
52    }
53
54    /// Set the package name
55    pub fn set_package(&mut self, package: &str) {
56        self.0.set("Package", package);
57    }
58
59    /// Get the version of the package
60    pub fn version(&self) -> Option<debversion::Version> {
61        self.0.get("Version").map(|s| s.parse().unwrap())
62    }
63
64    /// Set the version of the package
65    pub fn set_version(&mut self, version: debversion::Version) {
66        self.0.set("Version", &version.to_string());
67    }
68
69    /// Get the maintainer of the package
70    pub fn maintainer(&self) -> Option<String> {
71        self.0.get("Maintainer").map(|s| s.to_string())
72    }
73
74    /// Set the maintainer of the package
75    pub fn set_maintainer(&mut self, maintainer: &str) {
76        self.0.set("Maintainer", maintainer);
77    }
78
79    /// Get the uploaders of the package
80    pub fn uploaders(&self) -> Option<Vec<String>> {
81        self.0.get("Uploaders").map(|s| {
82            s.split(',')
83                .map(|s| s.trim().to_string())
84                .collect::<Vec<String>>()
85        })
86    }
87
88    /// Set the uploaders of the package
89    pub fn set_uploaders(&mut self, uploaders: Vec<String>) {
90        self.0.set("Uploaders", &uploaders.join(", "));
91    }
92
93    /// Get the standards version of the package
94    pub fn standards_version(&self) -> Option<String> {
95        self.0.get("Standards-Version").map(|s| s.to_string())
96    }
97
98    /// Set the standards version of the package
99    pub fn set_standards_version(&mut self, version: &str) {
100        self.0.set("Standards-Version", version);
101    }
102
103    /// Get the source format of the package
104    pub fn format(&self) -> Option<String> {
105        self.0.get("Format").map(|s| s.to_string())
106    }
107
108    /// Set the format of the package
109    pub fn set_format(&mut self, format: &str) {
110        self.0.set("Format", format);
111    }
112
113    /// Get the Vcs-Browser field
114    pub fn vcs_browser(&self) -> Option<String> {
115        self.0.get("Vcs-Browser").map(|s| s.to_string())
116    }
117
118    /// Set the Vcs-Browser field
119    pub fn set_vcs_browser(&mut self, url: &str) {
120        self.0.set("Vcs-Browser", url);
121    }
122
123    /// Get the Vcs-Git field
124    pub fn vcs_git(&self) -> Option<String> {
125        self.0.get("Vcs-Git").map(|s| s.to_string())
126    }
127
128    /// Set the Vcs-Git field
129    pub fn set_vcs_git(&mut self, url: &str) {
130        self.0.set("Vcs-Git", url);
131    }
132
133    /// Get the Vcs-Svn field
134    pub fn vcs_svn(&self) -> Option<String> {
135        self.0.get("Vcs-Svn").map(|s| s.to_string())
136    }
137
138    /// Set the Vcs-Svn field
139    pub fn set_vcs_svn(&mut self, url: &str) {
140        self.0.set("Vcs-Svn", url);
141    }
142
143    /// Get the Vcs-Hg field
144    pub fn vcs_hg(&self) -> Option<String> {
145        self.0.get("Vcs-Hg").map(|s| s.to_string())
146    }
147
148    /// Set the Vcs-Hg field
149    pub fn set_vcs_hg(&mut self, url: &str) {
150        self.0.set("Vcs-Hg", url);
151    }
152
153    /// Get the Vcs-Bzr field
154    pub fn vcs_bzr(&self) -> Option<String> {
155        self.0.get("Vcs-Bzr").map(|s| s.to_string())
156    }
157
158    /// Set the Vcs-Bzr field
159    pub fn set_vcs_bzr(&mut self, url: &str) {
160        self.0.set("Vcs-Bzr", url);
161    }
162
163    /// Get the Vcs-Arch field
164    pub fn vcs_arch(&self) -> Option<String> {
165        self.0.get("Vcs-Arch").map(|s| s.to_string())
166    }
167
168    /// Set the Vcs-Arch field
169    pub fn set_vcs_arch(&mut self, url: &str) {
170        self.0.set("Vcs-Arch", url);
171    }
172
173    /// Get the Vcs-Svk field
174    pub fn vcs_svk(&self) -> Option<String> {
175        self.0.get("Vcs-Svk").map(|s| s.to_string())
176    }
177
178    /// Set the Svk VCS
179    pub fn set_vcs_svk(&mut self, url: &str) {
180        self.0.set("Vcs-Svk", url);
181    }
182
183    /// Get the Darcs VCS
184    pub fn vcs_darcs(&self) -> Option<String> {
185        self.0.get("Vcs-Darcs").map(|s| s.to_string())
186    }
187
188    /// Set the Darcs VCS
189    pub fn set_vcs_darcs(&mut self, url: &str) {
190        self.0.set("Vcs-Darcs", url);
191    }
192
193    /// Get the Mtn VCS
194    pub fn vcs_mtn(&self) -> Option<String> {
195        self.0.get("Vcs-Mtn").map(|s| s.to_string())
196    }
197
198    /// Set the Mtn VCS
199    pub fn set_vcs_mtn(&mut self, url: &str) {
200        self.0.set("Vcs-Mtn", url);
201    }
202
203    /// Get the Cvs VCS
204    pub fn vcs_cvs(&self) -> Option<String> {
205        self.0.get("Vcs-Cvs").map(|s| s.to_string())
206    }
207
208    /// Set the Cvs VCS
209    pub fn set_vcs_cvs(&mut self, url: &str) {
210        self.0.set("Vcs-Cvs", url);
211    }
212
213    /// Get the build depends
214    pub fn build_depends(&self) -> Option<Relations> {
215        self.0.get("Build-Depends").map(|s| s.parse().unwrap())
216    }
217
218    /// Set the build depends
219    pub fn set_build_depends(&mut self, relations: Relations) {
220        self.0.set("Build-Depends", relations.to_string().as_str());
221    }
222
223    /// Get the arch-independent build depends
224    pub fn build_depends_indep(&self) -> Option<Relations> {
225        self.0
226            .get("Build-Depends-Indep")
227            .map(|s| s.parse().unwrap())
228    }
229
230    /// Set the arch-independent build depends
231    pub fn set_build_depends_indep(&mut self, relations: Relations) {
232        self.0.set("Build-Depends-Indep", &relations.to_string());
233    }
234
235    /// Get the arch-dependent build depends
236    pub fn build_depends_arch(&self) -> Option<Relations> {
237        self.0.get("Build-Depends-Arch").map(|s| s.parse().unwrap())
238    }
239
240    /// Set the arch-dependent build depends
241    pub fn set_build_depends_arch(&mut self, relations: Relations) {
242        self.0.set("Build-Depends-Arch", &relations.to_string());
243    }
244
245    /// Get the build conflicts
246    pub fn build_conflicts(&self) -> Option<Relations> {
247        self.0.get("Build-Conflicts").map(|s| s.parse().unwrap())
248    }
249
250    /// Set the build conflicts
251    pub fn set_build_conflicts(&mut self, relations: Relations) {
252        self.0.set("Build-Conflicts", &relations.to_string());
253    }
254
255    /// Get the build conflicts indep
256    pub fn build_conflicts_indep(&self) -> Option<Relations> {
257        self.0
258            .get("Build-Conflicts-Indep")
259            .map(|s| s.parse().unwrap())
260    }
261
262    /// Set the build conflicts indep
263    pub fn set_build_conflicts_indep(&mut self, relations: Relations) {
264        self.0.set("Build-Conflicts-Indep", &relations.to_string());
265    }
266
267    /// Get the build conflicts arch
268    pub fn build_conflicts_arch(&self) -> Option<Relations> {
269        self.0
270            .get("Build-Conflicts-Arch")
271            .map(|s| s.parse().unwrap())
272    }
273
274    /// Set the build conflicts arch
275    pub fn set_build_conflicts_arch(&mut self, relations: Relations) {
276        self.0.set("Build-Conflicts-Arch", &relations.to_string());
277    }
278
279    /// Get the binary relations
280    pub fn binary(&self) -> Option<Relations> {
281        self.0.get("Binary").map(|s| s.parse().unwrap())
282    }
283
284    /// Set the binary relations
285    pub fn set_binary(&mut self, relations: Relations) {
286        self.0.set("Binary", &relations.to_string());
287    }
288
289    /// Get the homepage of the package.
290    pub fn homepage(&self) -> Option<String> {
291        self.0.get("Homepage").map(|s| s.to_string())
292    }
293
294    /// Set the homepage of the package.
295    pub fn set_homepage(&mut self, url: &str) {
296        self.0.set("Homepage", url);
297    }
298
299    /// Get the section of the package.
300    pub fn section(&self) -> Option<String> {
301        self.0.get("Section").map(|s| s.to_string())
302    }
303
304    /// Set the section of the package.
305    pub fn set_section(&mut self, section: &str) {
306        self.0.set("Section", section);
307    }
308
309    /// Get the priority of the package.
310    pub fn priority(&self) -> Option<Priority> {
311        self.0.get("Priority").and_then(|v| v.parse().ok())
312    }
313
314    /// Set the priority of the package.
315    pub fn set_priority(&mut self, priority: Priority) {
316        self.0.set("Priority", priority.to_string().as_str());
317    }
318
319    /// The architecture of the package.
320    pub fn architecture(&self) -> Option<String> {
321        self.0.get("Architecture")
322    }
323
324    /// Set the architecture of the package.
325    pub fn set_architecture(&mut self, arch: &str) {
326        self.0.set("Architecture", arch);
327    }
328
329    /// Get the directory
330    pub fn directory(&self) -> Option<String> {
331        self.0.get("Directory").map(|s| s.to_string())
332    }
333
334    /// Set the directory
335    pub fn set_directory(&mut self, dir: &str) {
336        self.0.set("Directory", dir);
337    }
338
339    /// Get the test suite
340    pub fn testsuite(&self) -> Option<String> {
341        self.0.get("Testsuite").map(|s| s.to_string())
342    }
343
344    /// Set the testsuite
345    pub fn set_testsuite(&mut self, testsuite: &str) {
346        self.0.set("Testsuite", testsuite);
347    }
348
349    /// Get the files
350    pub fn files(&self) -> Vec<Md5Checksum> {
351        self.0
352            .get("Files")
353            .map(|s| {
354                s.lines()
355                    .map(|line| line.parse().unwrap())
356                    .collect::<Vec<Md5Checksum>>()
357            })
358            .unwrap_or_default()
359    }
360
361    /// Set the files
362    pub fn set_files(&mut self, files: Vec<Md5Checksum>) {
363        self.0.set(
364            "Files",
365            &files
366                .iter()
367                .map(|f| f.to_string())
368                .collect::<Vec<String>>()
369                .join("\n"),
370        );
371    }
372
373    /// Get the SHA1 checksums
374    pub fn checksums_sha1(&self) -> Vec<Sha1Checksum> {
375        self.0
376            .get("Checksums-Sha1")
377            .map(|s| {
378                s.lines()
379                    .map(|line| line.parse().unwrap())
380                    .collect::<Vec<Sha1Checksum>>()
381            })
382            .unwrap_or_default()
383    }
384
385    /// Set the SHA1 checksums
386    pub fn set_checksums_sha1(&mut self, checksums: Vec<Sha1Checksum>) {
387        self.0.set(
388            "Checksums-Sha1",
389            &checksums
390                .iter()
391                .map(|c| c.to_string())
392                .collect::<Vec<String>>()
393                .join("\n"),
394        );
395    }
396
397    /// Get the SHA256 checksums
398    pub fn checksums_sha256(&self) -> Vec<Sha256Checksum> {
399        self.0
400            .get("Checksums-Sha256")
401            .map(|s| {
402                s.lines()
403                    .map(|line| line.parse().unwrap())
404                    .collect::<Vec<Sha256Checksum>>()
405            })
406            .unwrap_or_default()
407    }
408
409    /// Set the SHA256 checksums
410    pub fn set_checksums_sha256(&mut self, checksums: Vec<Sha256Checksum>) {
411        self.0.set(
412            "Checksums-Sha256",
413            &checksums
414                .iter()
415                .map(|c| c.to_string())
416                .collect::<Vec<String>>()
417                .join("\n"),
418        );
419    }
420
421    /// Get the SHA512 checksums
422    pub fn checksums_sha512(&self) -> Vec<Sha512Checksum> {
423        self.0
424            .get("Checksums-Sha512")
425            .map(|s| {
426                s.lines()
427                    .map(|line| line.parse().unwrap())
428                    .collect::<Vec<Sha512Checksum>>()
429            })
430            .unwrap_or_default()
431    }
432
433    /// Set the SHA512 checksums
434    pub fn set_checksums_sha512(&mut self, checksums: Vec<Sha512Checksum>) {
435        self.0.set(
436            "Checksums-Sha512",
437            &checksums
438                .iter()
439                .map(|c| c.to_string())
440                .collect::<Vec<String>>()
441                .join("\n"),
442        );
443    }
444}
445
446impl std::str::FromStr for Source {
447    type Err = deb822_lossless::ParseError;
448
449    fn from_str(s: &str) -> Result<Self, Self::Err> {
450        Ok(Self(s.parse()?))
451    }
452}
453
454/// A package in the APT package manager.
455pub struct Package(deb822_lossless::Paragraph);
456
457#[cfg(feature = "python-debian")]
458impl pyo3::ToPyObject for Package {
459    fn to_object(&self, py: pyo3::Python) -> pyo3::PyObject {
460        use pyo3::prelude::*;
461        let d = self.0.to_object(py);
462
463        let m = py.import_bound("debian.deb822").unwrap();
464        let cls = m.getattr("Packages").unwrap();
465
466        cls.call1((d,)).unwrap().to_object(py)
467    }
468}
469
470#[cfg(feature = "python-debian")]
471impl pyo3::FromPyObject<'_> for Package {
472    fn extract_bound(ob: &pyo3::Bound<pyo3::PyAny>) -> pyo3::PyResult<Self> {
473        use pyo3::prelude::*;
474        Ok(Package(ob.extract()?))
475    }
476}
477
478impl Package {
479    /// Create a new package.
480    pub fn new(paragraph: deb822_lossless::Paragraph) -> Self {
481        Self(paragraph)
482    }
483
484    /// Get the name of the package.
485    pub fn name(&self) -> Option<String> {
486        self.0.get("Package").map(|s| s.to_string())
487    }
488
489    /// Set the name of the package.
490    pub fn set_name(&mut self, name: &str) {
491        self.0.set("Package", name);
492    }
493
494    /// Get the version of the package.
495    pub fn version(&self) -> Option<debversion::Version> {
496        self.0.get("Version").map(|s| s.parse().unwrap())
497    }
498
499    /// Set the version of the package.
500    pub fn set_version(&mut self, version: debversion::Version) {
501        self.0.set("Version", &version.to_string());
502    }
503
504    /// Get the installed size of the package in bytes.
505    pub fn installed_size(&self) -> Option<usize> {
506        self.0.get("Installed-Size").map(|s| s.parse().unwrap())
507    }
508
509    /// Set the installed size of the package in bytes.
510    pub fn set_installed_size(&mut self, size: usize) {
511        self.0.set("Installed-Size", &size.to_string());
512    }
513
514    /// Get the maintainer of the package.
515    pub fn maintainer(&self) -> Option<String> {
516        self.0.get("Maintainer").map(|s| s.to_string())
517    }
518
519    /// Set the maintainer of the package.
520    pub fn set_maintainer(&mut self, maintainer: &str) {
521        self.0.set("Maintainer", maintainer);
522    }
523
524    /// Get the architecture of the package.
525    pub fn architecture(&self) -> Option<String> {
526        self.0.get("Architecture").map(|s| s.to_string())
527    }
528
529    /// Set the architecture of the package.
530    pub fn set_architecture(&mut self, arch: &str) {
531        self.0.set("Architecture", arch);
532    }
533
534    /// Get the packages that this package depends on.
535    pub fn depends(&self) -> Option<Relations> {
536        self.0.get("Depends").map(|s| s.parse().unwrap())
537    }
538
539    /// Set the packages that this package depends on.
540    pub fn set_depends(&mut self, relations: Relations) {
541        self.0.set("Depends", &relations.to_string());
542    }
543
544    /// Get the packages that this package suggests.
545    pub fn recommends(&self) -> Option<Relations> {
546        self.0.get("Recommends").map(|s| s.parse().unwrap())
547    }
548
549    /// Set the packages that this package recommends.
550    pub fn set_recommends(&mut self, relations: Relations) {
551        self.0.set("Recommends", &relations.to_string());
552    }
553
554    /// Get the packages that this package suggests.
555    pub fn suggests(&self) -> Option<Relations> {
556        self.0.get("Suggests").map(|s| s.parse().unwrap())
557    }
558
559    /// Set the packages that this package suggests.
560    pub fn set_suggests(&mut self, relations: Relations) {
561        self.0.set("Suggests", &relations.to_string());
562    }
563
564    /// Get the packages that this package enhances.
565    pub fn enhances(&self) -> Option<Relations> {
566        self.0.get("Enhances").map(|s| s.parse().unwrap())
567    }
568
569    /// Set the packages that this package enhances.
570    pub fn set_enhances(&mut self, relations: Relations) {
571        self.0.set("Enhances", &relations.to_string());
572    }
573
574    /// Get the relations that this package pre-depends on.
575    pub fn pre_depends(&self) -> Option<Relations> {
576        self.0.get("Pre-Depends").map(|s| s.parse().unwrap())
577    }
578
579    /// Set the relations that this package pre-depends on.
580    pub fn set_pre_depends(&mut self, relations: Relations) {
581        self.0.set("Pre-Depends", &relations.to_string());
582    }
583
584    /// Get the relations that this package breaks.
585    pub fn breaks(&self) -> Option<Relations> {
586        self.0.get("Breaks").map(|s| s.parse().unwrap())
587    }
588
589    /// Set the relations that this package breaks.
590    pub fn set_breaks(&mut self, relations: Relations) {
591        self.0.set("Breaks", &relations.to_string());
592    }
593
594    /// Get the relations that this package conflicts with.
595    pub fn conflicts(&self) -> Option<Relations> {
596        self.0.get("Conflicts").map(|s| s.parse().unwrap())
597    }
598
599    /// Set the relations that this package conflicts with.
600    pub fn set_conflicts(&mut self, relations: Relations) {
601        self.0.set("Conflicts", &relations.to_string());
602    }
603
604    /// Get the relations that this package replaces.
605    pub fn replaces(&self) -> Option<Relations> {
606        self.0.get("Replaces").map(|s| s.parse().unwrap())
607    }
608
609    /// Set the relations that this package replaces.
610    pub fn set_replaces(&mut self, relations: Relations) {
611        self.0.set("Replaces", &relations.to_string());
612    }
613
614    /// Get the relations that this package provides.
615    pub fn provides(&self) -> Option<Relations> {
616        self.0.get("Provides").map(|s| s.parse().unwrap())
617    }
618
619    /// Set the relations that the package provides.
620    pub fn set_provides(&mut self, relations: Relations) {
621        self.0.set("Provides", &relations.to_string());
622    }
623
624    /// Get the section of the package.
625    pub fn section(&self) -> Option<String> {
626        self.0.get("Section").map(|s| s.to_string())
627    }
628
629    /// Set the section of the package.
630    pub fn set_section(&mut self, section: &str) {
631        self.0.set("Section", section);
632    }
633
634    /// Get the priority of the package.
635    pub fn priority(&self) -> Option<Priority> {
636        self.0.get("Priority").and_then(|v| v.parse().ok())
637    }
638
639    /// Set the priority of the package.
640    pub fn set_priority(&mut self, priority: Priority) {
641        self.0.set("Priority", priority.to_string().as_str());
642    }
643
644    /// Get the description of the package.
645    pub fn description(&self) -> Option<String> {
646        self.0.get("Description").map(|s| s.to_string())
647    }
648
649    /// Set the description of the package.
650    pub fn set_description(&mut self, description: &str) {
651        self.0.set("Description", description);
652    }
653
654    /// Get the upstream homepage of the package.
655    pub fn homepage(&self) -> Option<url::Url> {
656        self.0.get("Homepage").map(|s| s.parse().unwrap())
657    }
658
659    /// Set the upstream homepage of the package.
660    pub fn set_homepage(&mut self, url: &url::Url) {
661        self.0.set("Homepage", url.as_ref());
662    }
663
664    /// Get the source of the package.
665    pub fn source(&self) -> Option<String> {
666        self.0.get("Source").map(|s| s.to_string())
667    }
668
669    /// Set the source of the package.
670    pub fn set_source(&mut self, source: &str) {
671        self.0.set("Source", source);
672    }
673
674    /// Get the MD5 checksum of the description.
675    pub fn description_md5(&self) -> Option<String> {
676        self.0.get("Description-md5").map(|s| s.to_string())
677    }
678
679    /// Set the MD5 checksum of the description.
680    pub fn set_description_md5(&mut self, md5: &str) {
681        self.0.set("Description-md5", md5);
682    }
683
684    /// Get the tags of the package.
685    pub fn tags(&self, tag: &str) -> Option<Vec<String>> {
686        self.0
687            .get(tag)
688            .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
689    }
690
691    /// Set the tags of the package.
692    pub fn set_tags(&mut self, tag: &str, tags: Vec<String>) {
693        self.0.set(tag, &tags.join(", "));
694    }
695
696    /// Get the filename of the package.
697    pub fn filename(&self) -> Option<String> {
698        self.0.get("Filename").map(|s| s.to_string())
699    }
700
701    /// Set the filename of the package.
702    pub fn set_filename(&mut self, filename: &str) {
703        self.0.set("Filename", filename);
704    }
705
706    /// Get the size of the package.
707    pub fn size(&self) -> Option<usize> {
708        self.0.get("Size").map(|s| s.parse().unwrap())
709    }
710
711    /// Set the size of the package.
712    pub fn set_size(&mut self, size: usize) {
713        self.0.set("Size", &size.to_string());
714    }
715
716    /// Get the MD5 checksum.
717    pub fn md5sum(&self) -> Option<String> {
718        self.0.get("MD5sum").map(|s| s.to_string())
719    }
720
721    /// Set the MD5 checksum.
722    pub fn set_md5sum(&mut self, md5sum: &str) {
723        self.0.set("MD5sum", md5sum);
724    }
725
726    /// Get the SHA256 checksum.
727    pub fn sha256(&self) -> Option<String> {
728        self.0.get("SHA256").map(|s| s.to_string())
729    }
730
731    /// Set the SHA256 checksum.
732    pub fn set_sha256(&mut self, sha256: &str) {
733        self.0.set("SHA256", sha256);
734    }
735
736    /// Get the multi-arch field.
737    pub fn multi_arch(&self) -> Option<MultiArch> {
738        self.0.get("Multi-Arch").map(|s| s.parse().unwrap())
739    }
740
741    /// Set the multi-arch field.
742    pub fn set_multi_arch(&mut self, arch: MultiArch) {
743        self.0.set("Multi-Arch", arch.to_string().as_str());
744    }
745}
746
747impl std::str::FromStr for Package {
748    type Err = deb822_lossless::ParseError;
749
750    fn from_str(s: &str) -> Result<Self, Self::Err> {
751        Ok(Self(s.parse()?))
752    }
753}
754
755/// A release in the APT package manager.
756pub struct Release(deb822_lossless::Paragraph);
757
758#[cfg(feature = "python-debian")]
759impl pyo3::ToPyObject for Release {
760    fn to_object(&self, py: pyo3::Python) -> pyo3::PyObject {
761        use pyo3::prelude::*;
762        let d = self.0.to_object(py);
763
764        let m = py.import_bound("debian.deb822").unwrap();
765        let cls = m.getattr("Release").unwrap();
766
767        cls.call1((d,)).unwrap().to_object(py)
768    }
769}
770
771#[cfg(feature = "python-debian")]
772impl pyo3::FromPyObject<'_> for Release {
773    fn extract_bound(ob: &pyo3::Bound<pyo3::PyAny>) -> pyo3::PyResult<Self> {
774        use pyo3::prelude::*;
775        Ok(Release(ob.extract()?))
776    }
777}
778
779impl Release {
780    /// Create a new release
781    pub fn new(paragraph: deb822_lossless::Paragraph) -> Self {
782        Self(paragraph)
783    }
784
785    /// Get the origin of the release
786    pub fn origin(&self) -> Option<String> {
787        self.0.get("Origin").map(|s| s.to_string())
788    }
789
790    /// Set the origin of the release
791    pub fn set_origin(&mut self, origin: &str) {
792        self.0.set("Origin", origin);
793    }
794
795    /// Get the label of the release
796    pub fn label(&self) -> Option<String> {
797        self.0.get("Label").map(|s| s.to_string())
798    }
799
800    /// Set the label of the release
801    pub fn set_label(&mut self, label: &str) {
802        self.0.set("Label", label);
803    }
804
805    /// Get the suite of the release
806    pub fn suite(&self) -> Option<String> {
807        self.0.get("Suite").map(|s| s.to_string())
808    }
809
810    /// Set the suite of the release
811    pub fn set_suite(&mut self, suite: &str) {
812        self.0.set("Suite", suite);
813    }
814
815    /// Get the codename of the release
816    pub fn codename(&self) -> Option<String> {
817        self.0.get("Codename").map(|s| s.to_string())
818    }
819
820    /// Set the codename of the release
821    pub fn set_codename(&mut self, codename: &str) {
822        self.0.set("Codename", codename);
823    }
824
825    /// Get the URLs at which the changelogs can be found
826    pub fn changelogs(&self) -> Option<Vec<String>> {
827        self.0.get("Changelogs").map(|s| {
828            s.split(',')
829                .map(|s| s.trim().to_string())
830                .collect::<Vec<String>>()
831        })
832    }
833
834    /// Set the URLs at which the changelogs can be found
835    pub fn set_changelogs(&mut self, changelogs: Vec<String>) {
836        self.0.set("Changelogs", &changelogs.join(", "));
837    }
838
839    #[cfg(feature = "chrono")]
840    /// Get the date of the release
841    pub fn date(&self) -> Option<chrono::DateTime<chrono::FixedOffset>> {
842        self.0
843            .get("Date")
844            .as_ref()
845            .map(|s| chrono::DateTime::parse_from_rfc2822(s).unwrap())
846    }
847
848    #[cfg(feature = "chrono")]
849    /// Set the date of the release
850    pub fn set_date(&mut self, date: chrono::DateTime<chrono::FixedOffset>) {
851        self.0.set("Date", date.to_rfc2822().as_str());
852    }
853
854    #[cfg(feature = "chrono")]
855    /// Get the date until the release is valid
856    pub fn valid_until(&self) -> Option<chrono::DateTime<chrono::FixedOffset>> {
857        self.0
858            .get("Valid-Until")
859            .as_ref()
860            .map(|s| chrono::DateTime::parse_from_rfc2822(s).unwrap())
861    }
862
863    #[cfg(feature = "chrono")]
864    /// Set the date until the release is valid
865    pub fn set_valid_until(&mut self, date: chrono::DateTime<chrono::FixedOffset>) {
866        self.0.set("Valid-Until", date.to_rfc2822().as_str());
867    }
868
869    /// Get whether acquire by hash is enabled
870    pub fn acquire_by_hash(&self) -> bool {
871        self.0
872            .get("Acquire-By-Hash")
873            .map(|s| s == "yes")
874            .unwrap_or(false)
875    }
876
877    /// Set whether acquire by hash is enabled
878    pub fn set_acquire_by_hash(&mut self, acquire_by_hash: bool) {
879        self.0.set(
880            "Acquire-By-Hash",
881            if acquire_by_hash { "yes" } else { "no" },
882        );
883    }
884
885    /// Get whether the release has no support for architecture all
886    pub fn no_support_for_architecture_all(&self) -> bool {
887        self.0
888            .get("No-Support-For-Architecture-All")
889            .map(|s| s == "yes")
890            .unwrap_or(false)
891    }
892
893    /// Set whether the release has no support for architecture all
894    pub fn set_no_support_for_architecture_all(&mut self, no_support_for_architecture_all: bool) {
895        self.0.set(
896            "No-Support-For-Architecture-All",
897            if no_support_for_architecture_all {
898                "yes"
899            } else {
900                "no"
901            },
902        );
903    }
904
905    /// Get the architectures
906    pub fn architectures(&self) -> Option<Vec<String>> {
907        self.0.get("Architectures").map(|s| {
908            s.split_whitespace()
909                .map(|s| s.trim().to_string())
910                .collect::<Vec<String>>()
911        })
912    }
913
914    /// Set the architectures
915    pub fn set_architectures(&mut self, architectures: Vec<String>) {
916        self.0.set("Architectures", &architectures.join(" "));
917    }
918
919    /// Get the components
920    pub fn components(&self) -> Option<Vec<String>> {
921        self.0.get("Components").map(|s| {
922            s.split_whitespace()
923                .map(|s| s.trim().to_string())
924                .collect::<Vec<String>>()
925        })
926    }
927
928    /// Set the components
929    pub fn set_components(&mut self, components: Vec<String>) {
930        self.0.set("Components", &components.join(" "));
931    }
932
933    /// Get the description
934    pub fn description(&self) -> Option<String> {
935        self.0.get("Description").map(|s| s.to_string())
936    }
937
938    /// Set the description
939    pub fn set_description(&mut self, description: &str) {
940        self.0.set("Description", description);
941    }
942
943    /// Get the MD5 checksums
944    pub fn checksums_md5(&self) -> Vec<Md5Checksum> {
945        self.0
946            .get("MD5Sum")
947            .map(|s| {
948                s.lines()
949                    .map(|line| line.parse().unwrap())
950                    .collect::<Vec<Md5Checksum>>()
951            })
952            .unwrap_or_default()
953    }
954
955    /// Set the MD5 checksums
956    pub fn set_checksums_md5(&mut self, files: Vec<Md5Checksum>) {
957        self.0.set(
958            "MD5Sum",
959            &files
960                .iter()
961                .map(|f| f.to_string())
962                .collect::<Vec<String>>()
963                .join("\n"),
964        );
965    }
966
967    /// Get the SHA1 checksums
968    pub fn checksums_sha1(&self) -> Vec<Sha1Checksum> {
969        self.0
970            .get("SHA1")
971            .map(|s| {
972                s.lines()
973                    .map(|line| line.parse().unwrap())
974                    .collect::<Vec<Sha1Checksum>>()
975            })
976            .unwrap_or_default()
977    }
978
979    /// Set the SHA1 checksums
980    pub fn set_checksums_sha1(&mut self, checksums: Vec<Sha1Checksum>) {
981        self.0.set(
982            "SHA1",
983            &checksums
984                .iter()
985                .map(|c| c.to_string())
986                .collect::<Vec<String>>()
987                .join("\n"),
988        );
989    }
990
991    /// Get the SHA256 checksums
992    pub fn checksums_sha256(&self) -> Vec<Sha256Checksum> {
993        self.0
994            .get("SHA256")
995            .map(|s| {
996                s.lines()
997                    .map(|line| line.parse().unwrap())
998                    .collect::<Vec<Sha256Checksum>>()
999            })
1000            .unwrap_or_default()
1001    }
1002
1003    /// Set the SHA256 checksums
1004    pub fn set_checksums_sha256(&mut self, checksums: Vec<Sha256Checksum>) {
1005        self.0.set(
1006            "SHA256",
1007            &checksums
1008                .iter()
1009                .map(|c| c.to_string())
1010                .collect::<Vec<String>>()
1011                .join("\n"),
1012        );
1013    }
1014
1015    /// Get the SHA512 checksums
1016    pub fn checksums_sha512(&self) -> Vec<Sha512Checksum> {
1017        self.0
1018            .get("SHA512")
1019            .map(|s| {
1020                s.lines()
1021                    .map(|line| line.parse().unwrap())
1022                    .collect::<Vec<Sha512Checksum>>()
1023            })
1024            .unwrap_or_default()
1025    }
1026
1027    /// Set the SHA512 checksums
1028    pub fn set_checksums_sha512(&mut self, checksums: Vec<Sha512Checksum>) {
1029        self.0.set(
1030            "SHA512",
1031            &checksums
1032                .iter()
1033                .map(|c| c.to_string())
1034                .collect::<Vec<String>>()
1035                .join("\n"),
1036        );
1037    }
1038}
1039
1040impl std::str::FromStr for Release {
1041    type Err = deb822_lossless::ParseError;
1042
1043    fn from_str(s: &str) -> Result<Self, Self::Err> {
1044        Ok(Self(s.parse()?))
1045    }
1046}
1047
1048#[cfg(test)]
1049mod tests {
1050    use super::*;
1051    use crate::fields::PackageListEntry;
1052
1053    #[test]
1054    fn test_parse_package_list() {
1055        let s = "package1 binary section standard extra1=foo extra2=bar";
1056        let p: PackageListEntry = s.parse().unwrap();
1057        assert_eq!(p.package, "package1");
1058        assert_eq!(p.package_type, "binary");
1059        assert_eq!(p.section, "section");
1060        assert_eq!(p.priority, super::Priority::Standard);
1061        assert_eq!(p.extra.get("extra1"), Some(&"foo".to_string()));
1062        assert_eq!(p.extra.get("extra2"), Some(&"bar".to_string()));
1063    }
1064
1065    #[test]
1066    fn test_parse_package_list_no_extra() {
1067        let s = "package1 binary section standard";
1068        let p: PackageListEntry = s.parse().unwrap();
1069        assert_eq!(p.package, "package1");
1070        assert_eq!(p.package_type, "binary");
1071        assert_eq!(p.section, "section");
1072        assert_eq!(p.priority, super::Priority::Standard);
1073        assert!(p.extra.is_empty());
1074    }
1075
1076    #[test]
1077    fn test_files() {
1078        let s = "md5sum 1234 filename";
1079        let f: super::Md5Checksum = s.parse().unwrap();
1080        assert_eq!(f.md5sum, "md5sum");
1081        assert_eq!(f.size, 1234);
1082        assert_eq!(f.filename, "filename");
1083    }
1084
1085    #[test]
1086    fn test_sha1_checksum() {
1087        let s = "sha1 1234 filename";
1088        let f: super::Sha1Checksum = s.parse().unwrap();
1089        assert_eq!(f.sha1, "sha1");
1090        assert_eq!(f.size, 1234);
1091        assert_eq!(f.filename, "filename");
1092    }
1093
1094    #[test]
1095    fn test_sha256_checksum() {
1096        let s = "sha256 1234 filename";
1097        let f: super::Sha256Checksum = s.parse().unwrap();
1098        assert_eq!(f.sha256, "sha256");
1099        assert_eq!(f.size, 1234);
1100        assert_eq!(f.filename, "filename");
1101    }
1102
1103    #[test]
1104    fn test_sha512_checksum() {
1105        let s = "sha512 1234 filename";
1106        let f: super::Sha512Checksum = s.parse().unwrap();
1107        assert_eq!(f.sha512, "sha512");
1108        assert_eq!(f.size, 1234);
1109        assert_eq!(f.filename, "filename");
1110    }
1111
1112    #[test]
1113    fn test_source() {
1114        let s = r#"Package: foo
1115Version: 1.0
1116Maintainer: John Doe <john@example.com>
1117Uploaders: Jane Doe <jane@example.com>
1118Standards-Version: 3.9.8
1119Format: 3.0 (quilt)
1120Vcs-Browser: https://example.com/foo
1121Vcs-Git: https://example.com/foo.git
1122Build-Depends: debhelper (>= 9)
1123Build-Depends-Indep: python
1124Build-Depends-Arch: gcc
1125Build-Conflicts: bar
1126Build-Conflicts-Indep: python
1127Build-Conflicts-Arch: gcc
1128Binary: foo, bar
1129Homepage: https://example.com/foo
1130Section: devel
1131Priority: optional
1132Architecture: any
1133Directory: pool/main/f/foo
1134Files:
1135 25dcf3b4b6b3b3b3b3b3b3b3b3b3b3b3 1234 foo_1.0.tar.gz
1136Checksums-Sha1:
1137 b72b5fae3b3b3b3b3b3b3b3b3b3b3b3 1234 foo_1.0.tar.gz
1138"#;
1139        let p: super::Source = s.parse().unwrap();
1140        assert_eq!(p.package(), Some("foo".to_string()));
1141        assert_eq!(p.version(), Some("1.0".parse().unwrap()));
1142        assert_eq!(
1143            p.maintainer(),
1144            Some("John Doe <john@example.com>".to_string())
1145        );
1146        assert_eq!(
1147            p.uploaders(),
1148            Some(vec!["Jane Doe <jane@example.com>".to_string()])
1149        );
1150        assert_eq!(p.standards_version(), Some("3.9.8".to_string()));
1151        assert_eq!(p.format(), Some("3.0 (quilt)".to_string()));
1152        assert_eq!(p.vcs_browser(), Some("https://example.com/foo".to_string()));
1153        assert_eq!(p.vcs_git(), Some("https://example.com/foo.git".to_string()));
1154        assert_eq!(
1155            p.build_depends_indep().map(|x| x.to_string()),
1156            Some("python".parse().unwrap())
1157        );
1158        assert_eq!(p.build_depends(), Some("debhelper (>= 9)".parse().unwrap()));
1159        assert_eq!(p.build_depends_arch(), Some("gcc".parse().unwrap()));
1160        assert_eq!(p.build_conflicts(), Some("bar".parse().unwrap()));
1161        assert_eq!(p.build_conflicts_indep(), Some("python".parse().unwrap()));
1162        assert_eq!(p.build_conflicts_arch(), Some("gcc".parse().unwrap()));
1163        assert_eq!(p.binary(), Some("foo, bar".parse().unwrap()));
1164        assert_eq!(p.homepage(), Some("https://example.com/foo".to_string()));
1165        assert_eq!(p.section(), Some("devel".to_string()));
1166        assert_eq!(p.priority(), Some(super::Priority::Optional));
1167        assert_eq!(p.architecture(), Some("any".to_string()));
1168        assert_eq!(p.directory(), Some("pool/main/f/foo".to_string()));
1169        assert_eq!(p.files().len(), 1);
1170        assert_eq!(
1171            p.files()[0].md5sum,
1172            "25dcf3b4b6b3b3b3b3b3b3b3b3b3b3b3".to_string()
1173        );
1174        assert_eq!(p.files()[0].size, 1234);
1175        assert_eq!(p.files()[0].filename, "foo_1.0.tar.gz".to_string());
1176        assert_eq!(p.checksums_sha1().len(), 1);
1177        assert_eq!(
1178            p.checksums_sha1()[0].sha1,
1179            "b72b5fae3b3b3b3b3b3b3b3b3b3b3b3".to_string()
1180        );
1181    }
1182
1183    #[test]
1184    fn test_package() {
1185        let s = r#"Package: foo
1186Version: 1.0
1187Source: bar
1188Maintainer: John Doe <john@example.com>
1189Architecture: any
1190Depends: bar
1191Recommends: baz
1192Suggests: qux
1193Enhances: quux
1194Pre-Depends: quuz
1195Breaks: corge
1196Conflicts: grault
1197Replaces: garply
1198Provides: waldo
1199Section: devel
1200Priority: optional
1201Description: Foo is a bar
1202Homepage: https://example.com/foo
1203Description-md5: 1234
1204Tags: foo, bar
1205Filename: pool/main/f/foo/foo_1.0.deb
1206Size: 1234
1207Installed-Size: 1234
1208MD5sum: 1234
1209SHA256: 1234
1210Multi-Arch: same
1211"#;
1212        let p: super::Package = s.parse().unwrap();
1213        assert_eq!(p.name(), Some("foo".to_string()));
1214        assert_eq!(p.version(), Some("1.0".parse().unwrap()));
1215        assert_eq!(p.source(), Some("bar".to_string()));
1216        assert_eq!(
1217            p.maintainer(),
1218            Some("John Doe <john@example.com>".to_string())
1219        );
1220        assert_eq!(p.architecture(), Some("any".to_string()));
1221        assert_eq!(p.depends(), Some("bar".parse().unwrap()));
1222        assert_eq!(p.recommends(), Some("baz".parse().unwrap()));
1223        assert_eq!(p.suggests(), Some("qux".parse().unwrap()));
1224        assert_eq!(p.enhances(), Some("quux".parse().unwrap()));
1225        assert_eq!(p.pre_depends(), Some("quuz".parse().unwrap()));
1226        assert_eq!(p.breaks(), Some("corge".parse().unwrap()));
1227        assert_eq!(p.conflicts(), Some("grault".parse().unwrap()));
1228        assert_eq!(p.replaces(), Some("garply".parse().unwrap()));
1229        assert_eq!(p.provides(), Some("waldo".parse().unwrap()));
1230        assert_eq!(p.section(), Some("devel".to_string()));
1231        assert_eq!(p.priority(), Some(super::Priority::Optional));
1232        assert_eq!(p.description(), Some("Foo is a bar".to_string()));
1233        assert_eq!(
1234            p.homepage(),
1235            Some(url::Url::parse("https://example.com/foo").unwrap())
1236        );
1237        assert_eq!(p.description_md5(), Some("1234".to_string()));
1238        assert_eq!(
1239            p.tags("Tags"),
1240            Some(vec!["foo".to_string(), "bar".to_string()])
1241        );
1242        assert_eq!(
1243            p.filename(),
1244            Some("pool/main/f/foo/foo_1.0.deb".to_string())
1245        );
1246        assert_eq!(p.size(), Some(1234));
1247        assert_eq!(p.installed_size(), Some(1234));
1248        assert_eq!(p.md5sum(), Some("1234".to_string()));
1249        assert_eq!(p.sha256(), Some("1234".to_string()));
1250        assert_eq!(p.multi_arch(), Some(MultiArch::Same));
1251    }
1252
1253    #[test]
1254    fn test_release() {
1255        let s = include_str!("../testdata/Release");
1256        let release: super::Release = s.parse().unwrap();
1257
1258        assert_eq!(release.origin(), Some("Debian".to_string()));
1259        assert_eq!(release.label(), Some("Debian".to_string()));
1260        assert_eq!(release.suite(), Some("testing".to_string()));
1261        assert_eq!(
1262            release.architectures(),
1263            vec![
1264                "all".to_string(),
1265                "amd64".to_string(),
1266                "arm64".to_string(),
1267                "armel".to_string(),
1268                "armhf".to_string()
1269            ]
1270            .into()
1271        );
1272        assert_eq!(
1273            release.components(),
1274            vec![
1275                "main".to_string(),
1276                "contrib".to_string(),
1277                "non-free-firmware".to_string(),
1278                "non-free".to_string()
1279            ]
1280            .into()
1281        );
1282        assert_eq!(
1283            release.description(),
1284            Some("Debian x.y Testing distribution - Not Released".to_string())
1285        );
1286        assert_eq!(318, release.checksums_md5().len());
1287    }
1288}