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