1use crate::fields::{
3 Md5Checksum, MultiArch, Priority, Sha1Checksum, Sha256Checksum, Sha512Checksum,
4};
5use crate::lossless::relations::Relations;
6use rowan::ast::AstNode;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Source(deb822_lossless::Paragraph);
11
12unsafe impl Send for Source {}
15unsafe impl Sync for Source {}
16
17#[cfg(feature = "python-debian")]
18impl<'py> pyo3::IntoPyObject<'py> for Source {
19 type Target = pyo3::PyAny;
20 type Output = pyo3::Bound<'py, Self::Target>;
21 type Error = pyo3::PyErr;
22
23 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
24 use pyo3::prelude::*;
25 let d = self.0.into_pyobject(py)?;
26
27 let m = py.import("debian.deb822")?;
28 let cls = m.getattr("Sources")?;
29
30 cls.call1((d,))
31 }
32}
33
34#[cfg(feature = "python-debian")]
35impl<'py> pyo3::IntoPyObject<'py> for &Source {
36 type Target = pyo3::PyAny;
37 type Output = pyo3::Bound<'py, Self::Target>;
38 type Error = pyo3::PyErr;
39
40 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
41 use pyo3::prelude::*;
42 let d = (&self.0).into_pyobject(py)?;
43
44 let m = py.import("debian.deb822")?;
45 let cls = m.getattr("Sources")?;
46
47 cls.call1((d,))
48 }
49}
50
51#[cfg(feature = "python-debian")]
52impl<'py> pyo3::FromPyObject<'_, 'py> for Source {
53 type Error = pyo3::PyErr;
54
55 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
56 Ok(Source(ob.extract()?))
57 }
58}
59
60impl From<deb822_lossless::Paragraph> for Source {
61 fn from(paragraph: deb822_lossless::Paragraph) -> Self {
62 Self(paragraph)
63 }
64}
65
66impl Default for Source {
67 fn default() -> Self {
68 Self(deb822_lossless::Paragraph::new())
69 }
70}
71
72impl Source {
73 pub fn new() -> Self {
75 Self(deb822_lossless::Paragraph::new())
76 }
77
78 pub fn parse(text: &str) -> deb822_lossless::Parse<Source> {
82 let deb822_parse = deb822_lossless::Deb822::parse(text);
85
86 let green = deb822_parse.green().clone();
88 let mut errors = deb822_parse.errors().to_vec();
89
90 if errors.is_empty() {
92 let deb822 = deb822_parse.tree();
93 let paragraph_count = deb822.paragraphs().count();
94 if paragraph_count == 0 {
95 errors.push("No paragraphs found".to_string());
96 } else if paragraph_count > 1 {
97 errors.push("Multiple paragraphs found, expected one".to_string());
98 }
99 }
100
101 deb822_lossless::Parse::new(green, errors)
102 }
103
104 pub fn package(&self) -> Option<String> {
106 self.0.get("Package").map(|s| s.to_string())
107 }
108
109 pub fn set_package(&mut self, package: &str) {
111 self.0.set("Package", package);
112 }
113
114 pub fn version(&self) -> Option<debversion::Version> {
116 self.0.get("Version").map(|s| s.parse().unwrap())
117 }
118
119 pub fn set_version(&mut self, version: debversion::Version) {
121 self.0.set("Version", &version.to_string());
122 }
123
124 pub fn maintainer(&self) -> Option<String> {
126 self.0.get("Maintainer").map(|s| s.to_string())
127 }
128
129 pub fn set_maintainer(&mut self, maintainer: &str) {
131 self.0.set("Maintainer", maintainer);
132 }
133
134 pub fn uploaders(&self) -> Option<Vec<String>> {
136 self.0.get("Uploaders").map(|s| {
137 s.split(',')
138 .map(|s| s.trim().to_string())
139 .collect::<Vec<String>>()
140 })
141 }
142
143 pub fn set_uploaders(&mut self, uploaders: Vec<String>) {
145 self.0.set("Uploaders", &uploaders.join(", "));
146 }
147
148 pub fn standards_version(&self) -> Option<String> {
150 self.0.get("Standards-Version").map(|s| s.to_string())
151 }
152
153 pub fn set_standards_version(&mut self, version: &str) {
155 self.0.set("Standards-Version", version);
156 }
157
158 pub fn format(&self) -> Option<String> {
160 self.0.get("Format").map(|s| s.to_string())
161 }
162
163 pub fn set_format(&mut self, format: &str) {
165 self.0.set("Format", format);
166 }
167
168 pub fn vcs_browser(&self) -> Option<String> {
170 self.0.get("Vcs-Browser").map(|s| s.to_string())
171 }
172
173 pub fn set_vcs_browser(&mut self, url: &str) {
175 self.0.set("Vcs-Browser", url);
176 }
177
178 pub fn vcs_git(&self) -> Option<String> {
180 self.0.get("Vcs-Git").map(|s| s.to_string())
181 }
182
183 pub fn set_vcs_git(&mut self, url: &str) {
185 self.0.set("Vcs-Git", url);
186 }
187
188 pub fn vcs_svn(&self) -> Option<String> {
190 self.0.get("Vcs-Svn").map(|s| s.to_string())
191 }
192
193 pub fn set_vcs_svn(&mut self, url: &str) {
195 self.0.set("Vcs-Svn", url);
196 }
197
198 pub fn vcs_hg(&self) -> Option<String> {
200 self.0.get("Vcs-Hg").map(|s| s.to_string())
201 }
202
203 pub fn set_vcs_hg(&mut self, url: &str) {
205 self.0.set("Vcs-Hg", url);
206 }
207
208 pub fn vcs_bzr(&self) -> Option<String> {
210 self.0.get("Vcs-Bzr").map(|s| s.to_string())
211 }
212
213 pub fn set_vcs_bzr(&mut self, url: &str) {
215 self.0.set("Vcs-Bzr", url);
216 }
217
218 pub fn vcs_arch(&self) -> Option<String> {
220 self.0.get("Vcs-Arch").map(|s| s.to_string())
221 }
222
223 pub fn set_vcs_arch(&mut self, url: &str) {
225 self.0.set("Vcs-Arch", url);
226 }
227
228 pub fn vcs_svk(&self) -> Option<String> {
230 self.0.get("Vcs-Svk").map(|s| s.to_string())
231 }
232
233 pub fn set_vcs_svk(&mut self, url: &str) {
235 self.0.set("Vcs-Svk", url);
236 }
237
238 pub fn vcs_darcs(&self) -> Option<String> {
240 self.0.get("Vcs-Darcs").map(|s| s.to_string())
241 }
242
243 pub fn set_vcs_darcs(&mut self, url: &str) {
245 self.0.set("Vcs-Darcs", url);
246 }
247
248 pub fn vcs_mtn(&self) -> Option<String> {
250 self.0.get("Vcs-Mtn").map(|s| s.to_string())
251 }
252
253 pub fn set_vcs_mtn(&mut self, url: &str) {
255 self.0.set("Vcs-Mtn", url);
256 }
257
258 pub fn vcs_cvs(&self) -> Option<String> {
260 self.0.get("Vcs-Cvs").map(|s| s.to_string())
261 }
262
263 pub fn set_vcs_cvs(&mut self, url: &str) {
265 self.0.set("Vcs-Cvs", url);
266 }
267
268 pub fn build_depends(&self) -> Option<Relations> {
270 self.0
271 .get_with_comments("Build-Depends")
272 .map(|s| s.parse().unwrap())
273 }
274
275 pub fn set_build_depends(&mut self, relations: Relations) {
277 self.0.set("Build-Depends", relations.to_string().as_str());
278 }
279
280 pub fn build_depends_indep(&self) -> Option<Relations> {
282 self.0
283 .get_with_comments("Build-Depends-Indep")
284 .map(|s| s.parse().unwrap())
285 }
286
287 pub fn set_build_depends_indep(&mut self, relations: Relations) {
289 self.0.set("Build-Depends-Indep", &relations.to_string());
290 }
291
292 pub fn build_depends_arch(&self) -> Option<Relations> {
294 self.0
295 .get_with_comments("Build-Depends-Arch")
296 .map(|s| s.parse().unwrap())
297 }
298
299 pub fn set_build_depends_arch(&mut self, relations: Relations) {
301 self.0.set("Build-Depends-Arch", &relations.to_string());
302 }
303
304 pub fn build_conflicts(&self) -> Option<Relations> {
306 self.0
307 .get_with_comments("Build-Conflicts")
308 .map(|s| s.parse().unwrap())
309 }
310
311 pub fn set_build_conflicts(&mut self, relations: Relations) {
313 self.0.set("Build-Conflicts", &relations.to_string());
314 }
315
316 pub fn build_conflicts_indep(&self) -> Option<Relations> {
318 self.0
319 .get_with_comments("Build-Conflicts-Indep")
320 .map(|s| s.parse().unwrap())
321 }
322
323 pub fn set_build_conflicts_indep(&mut self, relations: Relations) {
325 self.0.set("Build-Conflicts-Indep", &relations.to_string());
326 }
327
328 pub fn build_conflicts_arch(&self) -> Option<Relations> {
330 self.0
331 .get_with_comments("Build-Conflicts-Arch")
332 .map(|s| s.parse().unwrap())
333 }
334
335 pub fn set_build_conflicts_arch(&mut self, relations: Relations) {
337 self.0.set("Build-Conflicts-Arch", &relations.to_string());
338 }
339
340 pub fn binary(&self) -> Option<Relations> {
342 self.0.get("Binary").map(|s| s.parse().unwrap())
343 }
344
345 pub fn set_binary(&mut self, relations: Relations) {
347 self.0.set("Binary", &relations.to_string());
348 }
349
350 pub fn homepage(&self) -> Option<String> {
352 self.0.get("Homepage").map(|s| s.to_string())
353 }
354
355 pub fn set_homepage(&mut self, url: &str) {
357 self.0.set("Homepage", url);
358 }
359
360 pub fn section(&self) -> Option<String> {
362 self.0.get("Section").map(|s| s.to_string())
363 }
364
365 pub fn set_section(&mut self, section: &str) {
367 self.0.set("Section", section);
368 }
369
370 pub fn priority(&self) -> Option<Priority> {
372 self.0.get("Priority").and_then(|v| v.parse().ok())
373 }
374
375 pub fn set_priority(&mut self, priority: Priority) {
377 self.0.set("Priority", priority.to_string().as_str());
378 }
379
380 pub fn architecture(&self) -> Option<String> {
382 self.0.get("Architecture")
383 }
384
385 pub fn set_architecture(&mut self, arch: &str) {
387 self.0.set("Architecture", arch);
388 }
389
390 pub fn directory(&self) -> Option<String> {
392 self.0.get("Directory").map(|s| s.to_string())
393 }
394
395 pub fn set_directory(&mut self, dir: &str) {
397 self.0.set("Directory", dir);
398 }
399
400 pub fn testsuite(&self) -> Option<String> {
402 self.0.get("Testsuite").map(|s| s.to_string())
403 }
404
405 pub fn set_testsuite(&mut self, testsuite: &str) {
407 self.0.set("Testsuite", testsuite);
408 }
409
410 pub fn files(&self) -> Vec<Md5Checksum> {
412 self.0
413 .get("Files")
414 .map(|s| {
415 s.lines()
416 .map(|line| line.parse().unwrap())
417 .collect::<Vec<Md5Checksum>>()
418 })
419 .unwrap_or_default()
420 }
421
422 pub fn set_files(&mut self, files: Vec<Md5Checksum>) {
424 self.0.set(
425 "Files",
426 &files
427 .iter()
428 .map(|f| f.to_string())
429 .collect::<Vec<String>>()
430 .join("\n"),
431 );
432 }
433
434 pub fn checksums_sha1(&self) -> Vec<Sha1Checksum> {
436 self.0
437 .get("Checksums-Sha1")
438 .map(|s| {
439 s.lines()
440 .map(|line| line.parse().unwrap())
441 .collect::<Vec<Sha1Checksum>>()
442 })
443 .unwrap_or_default()
444 }
445
446 pub fn set_checksums_sha1(&mut self, checksums: Vec<Sha1Checksum>) {
448 self.0.set(
449 "Checksums-Sha1",
450 &checksums
451 .iter()
452 .map(|c| c.to_string())
453 .collect::<Vec<String>>()
454 .join("\n"),
455 );
456 }
457
458 pub fn checksums_sha256(&self) -> Vec<Sha256Checksum> {
460 self.0
461 .get("Checksums-Sha256")
462 .map(|s| {
463 s.lines()
464 .map(|line| line.parse().unwrap())
465 .collect::<Vec<Sha256Checksum>>()
466 })
467 .unwrap_or_default()
468 }
469
470 pub fn set_checksums_sha256(&mut self, checksums: Vec<Sha256Checksum>) {
472 self.0.set(
473 "Checksums-Sha256",
474 &checksums
475 .iter()
476 .map(|c| c.to_string())
477 .collect::<Vec<String>>()
478 .join("\n"),
479 );
480 }
481
482 pub fn checksums_sha512(&self) -> Vec<Sha512Checksum> {
484 self.0
485 .get("Checksums-Sha512")
486 .map(|s| {
487 s.lines()
488 .map(|line| line.parse().unwrap())
489 .collect::<Vec<Sha512Checksum>>()
490 })
491 .unwrap_or_default()
492 }
493
494 pub fn set_checksums_sha512(&mut self, checksums: Vec<Sha512Checksum>) {
496 self.0.set(
497 "Checksums-Sha512",
498 &checksums
499 .iter()
500 .map(|c| c.to_string())
501 .collect::<Vec<String>>()
502 .join("\n"),
503 );
504 }
505}
506
507impl std::str::FromStr for Source {
508 type Err = deb822_lossless::ParseError;
509
510 fn from_str(s: &str) -> Result<Self, Self::Err> {
511 Source::parse(s).to_result()
512 }
513}
514
515impl AstNode for Source {
516 type Language = deb822_lossless::Lang;
517
518 fn can_cast(kind: <Self::Language as rowan::Language>::Kind) -> bool {
519 deb822_lossless::Paragraph::can_cast(kind) || deb822_lossless::Deb822::can_cast(kind)
521 }
522
523 fn cast(syntax: rowan::SyntaxNode<Self::Language>) -> Option<Self> {
524 if let Some(para) = deb822_lossless::Paragraph::cast(syntax.clone()) {
526 Some(Source(para))
527 } else if let Some(deb822) = deb822_lossless::Deb822::cast(syntax) {
528 deb822.paragraphs().next().map(Source)
530 } else {
531 None
532 }
533 }
534
535 fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
536 self.0.syntax()
537 }
538}
539
540impl std::fmt::Display for Source {
541 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
542 write!(f, "{}", self.0)
543 }
544}
545
546#[derive(Debug, Clone, PartialEq, Eq)]
548pub struct Package(deb822_lossless::Paragraph);
549
550unsafe impl Send for Package {}
553unsafe impl Sync for Package {}
554
555#[cfg(feature = "python-debian")]
556impl<'py> pyo3::IntoPyObject<'py> for Package {
557 type Target = pyo3::PyAny;
558 type Output = pyo3::Bound<'py, Self::Target>;
559 type Error = pyo3::PyErr;
560
561 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
562 use pyo3::prelude::*;
563 let d = self.0.into_pyobject(py)?;
564
565 let m = py.import("debian.deb822")?;
566 let cls = m.getattr("Packages")?;
567
568 cls.call1((d,))
569 }
570}
571
572#[cfg(feature = "python-debian")]
573impl<'py> pyo3::IntoPyObject<'py> for &Package {
574 type Target = pyo3::PyAny;
575 type Output = pyo3::Bound<'py, Self::Target>;
576 type Error = pyo3::PyErr;
577
578 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
579 use pyo3::prelude::*;
580 let d = (&self.0).into_pyobject(py)?;
581
582 let m = py.import("debian.deb822")?;
583 let cls = m.getattr("Packages")?;
584
585 cls.call1((d,))
586 }
587}
588
589#[cfg(feature = "python-debian")]
590impl<'py> pyo3::FromPyObject<'_, 'py> for Package {
591 type Error = pyo3::PyErr;
592
593 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
594 Ok(Package(ob.extract()?))
595 }
596}
597
598impl Package {
599 pub fn new(paragraph: deb822_lossless::Paragraph) -> Self {
601 Self(paragraph)
602 }
603
604 pub fn parse(text: &str) -> deb822_lossless::Parse<Package> {
608 let deb822_parse = deb822_lossless::Deb822::parse(text);
609 let green = deb822_parse.green().clone();
610 let mut errors = deb822_parse.errors().to_vec();
611
612 if errors.is_empty() {
614 let deb822 = deb822_parse.tree();
615 let paragraph_count = deb822.paragraphs().count();
616 if paragraph_count == 0 {
617 errors.push("No paragraphs found".to_string());
618 } else if paragraph_count > 1 {
619 errors.push("Multiple paragraphs found, expected one".to_string());
620 }
621 }
622
623 deb822_lossless::Parse::new(green, errors)
624 }
625
626 pub fn name(&self) -> Option<String> {
628 self.0.get("Package").map(|s| s.to_string())
629 }
630
631 pub fn set_name(&mut self, name: &str) {
633 self.0.set("Package", name);
634 }
635
636 pub fn version(&self) -> Option<debversion::Version> {
638 self.0.get("Version").map(|s| s.parse().unwrap())
639 }
640
641 pub fn set_version(&mut self, version: debversion::Version) {
643 self.0.set("Version", &version.to_string());
644 }
645
646 pub fn installed_size(&self) -> Option<usize> {
648 self.0.get("Installed-Size").map(|s| s.parse().unwrap())
649 }
650
651 pub fn set_installed_size(&mut self, size: usize) {
653 self.0.set("Installed-Size", &size.to_string());
654 }
655
656 pub fn maintainer(&self) -> Option<String> {
658 self.0.get("Maintainer").map(|s| s.to_string())
659 }
660
661 pub fn set_maintainer(&mut self, maintainer: &str) {
663 self.0.set("Maintainer", maintainer);
664 }
665
666 pub fn architecture(&self) -> Option<String> {
668 self.0.get("Architecture").map(|s| s.to_string())
669 }
670
671 pub fn set_architecture(&mut self, arch: &str) {
673 self.0.set("Architecture", arch);
674 }
675
676 pub fn depends(&self) -> Option<Relations> {
678 self.0
679 .get_with_comments("Depends")
680 .map(|s| s.parse().unwrap())
681 }
682
683 pub fn set_depends(&mut self, relations: Relations) {
685 self.0.set("Depends", &relations.to_string());
686 }
687
688 pub fn recommends(&self) -> Option<Relations> {
690 self.0
691 .get_with_comments("Recommends")
692 .map(|s| s.parse().unwrap())
693 }
694
695 pub fn set_recommends(&mut self, relations: Relations) {
697 self.0.set("Recommends", &relations.to_string());
698 }
699
700 pub fn suggests(&self) -> Option<Relations> {
702 self.0
703 .get_with_comments("Suggests")
704 .map(|s| s.parse().unwrap())
705 }
706
707 pub fn set_suggests(&mut self, relations: Relations) {
709 self.0.set("Suggests", &relations.to_string());
710 }
711
712 pub fn enhances(&self) -> Option<Relations> {
714 self.0
715 .get_with_comments("Enhances")
716 .map(|s| s.parse().unwrap())
717 }
718
719 pub fn set_enhances(&mut self, relations: Relations) {
721 self.0.set("Enhances", &relations.to_string());
722 }
723
724 pub fn pre_depends(&self) -> Option<Relations> {
726 self.0
727 .get_with_comments("Pre-Depends")
728 .map(|s| s.parse().unwrap())
729 }
730
731 pub fn set_pre_depends(&mut self, relations: Relations) {
733 self.0.set("Pre-Depends", &relations.to_string());
734 }
735
736 pub fn breaks(&self) -> Option<Relations> {
738 self.0
739 .get_with_comments("Breaks")
740 .map(|s| s.parse().unwrap())
741 }
742
743 pub fn set_breaks(&mut self, relations: Relations) {
745 self.0.set("Breaks", &relations.to_string());
746 }
747
748 pub fn conflicts(&self) -> Option<Relations> {
750 self.0
751 .get_with_comments("Conflicts")
752 .map(|s| s.parse().unwrap())
753 }
754
755 pub fn set_conflicts(&mut self, relations: Relations) {
757 self.0.set("Conflicts", &relations.to_string());
758 }
759
760 pub fn replaces(&self) -> Option<Relations> {
762 self.0
763 .get_with_comments("Replaces")
764 .map(|s| s.parse().unwrap())
765 }
766
767 pub fn set_replaces(&mut self, relations: Relations) {
769 self.0.set("Replaces", &relations.to_string());
770 }
771
772 pub fn provides(&self) -> Option<Relations> {
774 self.0
775 .get_with_comments("Provides")
776 .map(|s| s.parse().unwrap())
777 }
778
779 pub fn set_provides(&mut self, relations: Relations) {
781 self.0.set("Provides", &relations.to_string());
782 }
783
784 pub fn section(&self) -> Option<String> {
786 self.0.get("Section").map(|s| s.to_string())
787 }
788
789 pub fn set_section(&mut self, section: &str) {
791 self.0.set("Section", section);
792 }
793
794 pub fn priority(&self) -> Option<Priority> {
796 self.0.get("Priority").and_then(|v| v.parse().ok())
797 }
798
799 pub fn set_priority(&mut self, priority: Priority) {
801 self.0.set("Priority", priority.to_string().as_str());
802 }
803
804 pub fn description(&self) -> Option<String> {
806 self.0.get_multiline("Description")
807 }
808
809 pub fn set_description(&mut self, description: &str) {
811 self.0.set("Description", description);
812 }
813
814 pub fn homepage(&self) -> Option<url::Url> {
816 self.0.get("Homepage").map(|s| s.parse().unwrap())
817 }
818
819 pub fn set_homepage(&mut self, url: &url::Url) {
821 self.0.set("Homepage", url.as_ref());
822 }
823
824 pub fn source(&self) -> Option<String> {
826 self.0.get("Source").map(|s| s.to_string())
827 }
828
829 pub fn set_source(&mut self, source: &str) {
831 self.0.set("Source", source);
832 }
833
834 pub fn description_md5(&self) -> Option<String> {
836 self.0.get("Description-md5").map(|s| s.to_string())
837 }
838
839 pub fn set_description_md5(&mut self, md5: &str) {
841 self.0.set("Description-md5", md5);
842 }
843
844 pub fn tags(&self, tag: &str) -> Option<Vec<String>> {
846 self.0
847 .get(tag)
848 .map(|s| s.split(',').map(|s| s.trim().to_string()).collect())
849 }
850
851 pub fn set_tags(&mut self, tag: &str, tags: Vec<String>) {
853 self.0.set(tag, &tags.join(", "));
854 }
855
856 pub fn filename(&self) -> Option<String> {
858 self.0.get("Filename").map(|s| s.to_string())
859 }
860
861 pub fn set_filename(&mut self, filename: &str) {
863 self.0.set("Filename", filename);
864 }
865
866 pub fn size(&self) -> Option<usize> {
868 self.0.get("Size").map(|s| s.parse().unwrap())
869 }
870
871 pub fn set_size(&mut self, size: usize) {
873 self.0.set("Size", &size.to_string());
874 }
875
876 pub fn md5sum(&self) -> Option<String> {
878 self.0.get("MD5sum").map(|s| s.to_string())
879 }
880
881 pub fn set_md5sum(&mut self, md5sum: &str) {
883 self.0.set("MD5sum", md5sum);
884 }
885
886 pub fn sha1(&self) -> Option<String> {
888 self.0.get("SHA1").map(|s| s.to_string())
889 }
890
891 pub fn set_sha1(&mut self, sha1: &str) {
893 self.0.set("SHA1", sha1);
894 }
895
896 pub fn sha256(&self) -> Option<String> {
898 self.0.get("SHA256").map(|s| s.to_string())
899 }
900
901 pub fn set_sha256(&mut self, sha256: &str) {
903 self.0.set("SHA256", sha256);
904 }
905
906 pub fn sha512(&self) -> Option<String> {
908 self.0.get("SHA512").map(|s| s.to_string())
909 }
910
911 pub fn set_sha512(&mut self, sha512: &str) {
913 self.0.set("SHA512", sha512);
914 }
915
916 pub fn multi_arch(&self) -> Option<MultiArch> {
918 self.0.get("Multi-Arch").map(|s| s.parse().unwrap())
919 }
920
921 pub fn set_multi_arch(&mut self, arch: MultiArch) {
923 self.0.set("Multi-Arch", arch.to_string().as_str());
924 }
925}
926
927impl Default for Package {
928 fn default() -> Self {
929 Self(deb822_lossless::Paragraph::new())
930 }
931}
932
933impl std::fmt::Display for Package {
934 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
935 write!(f, "{}", self.0)
936 }
937}
938
939impl std::str::FromStr for Package {
940 type Err = deb822_lossless::ParseError;
941
942 fn from_str(s: &str) -> Result<Self, Self::Err> {
943 Package::parse(s).to_result()
944 }
945}
946
947impl AstNode for Package {
948 type Language = deb822_lossless::Lang;
949
950 fn can_cast(kind: <Self::Language as rowan::Language>::Kind) -> bool {
951 deb822_lossless::Paragraph::can_cast(kind) || deb822_lossless::Deb822::can_cast(kind)
952 }
953
954 fn cast(syntax: rowan::SyntaxNode<Self::Language>) -> Option<Self> {
955 if let Some(para) = deb822_lossless::Paragraph::cast(syntax.clone()) {
956 Some(Package(para))
957 } else if let Some(deb822) = deb822_lossless::Deb822::cast(syntax) {
958 deb822.paragraphs().next().map(Package)
959 } else {
960 None
961 }
962 }
963
964 fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
965 self.0.syntax()
966 }
967}
968
969#[derive(Clone)]
971pub struct Release(deb822_lossless::Paragraph);
972
973unsafe impl Send for Release {}
976unsafe impl Sync for Release {}
977
978#[cfg(feature = "python-debian")]
979impl<'py> pyo3::IntoPyObject<'py> for Release {
980 type Target = pyo3::PyAny;
981 type Output = pyo3::Bound<'py, Self::Target>;
982 type Error = pyo3::PyErr;
983
984 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
985 use pyo3::prelude::*;
986 let d = self.0.into_pyobject(py)?;
987
988 let m = py.import("debian.deb822")?;
989 let cls = m.getattr("Release")?;
990
991 cls.call1((d,))
992 }
993}
994
995#[cfg(feature = "python-debian")]
996impl<'py> pyo3::IntoPyObject<'py> for &Release {
997 type Target = pyo3::PyAny;
998 type Output = pyo3::Bound<'py, Self::Target>;
999 type Error = pyo3::PyErr;
1000
1001 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
1002 use pyo3::prelude::*;
1003 let d = (&self.0).into_pyobject(py)?;
1004
1005 let m = py.import("debian.deb822")?;
1006 let cls = m.getattr("Release")?;
1007
1008 cls.call1((d,))
1009 }
1010}
1011
1012#[cfg(feature = "python-debian")]
1013impl<'py> pyo3::FromPyObject<'_, 'py> for Release {
1014 type Error = pyo3::PyErr;
1015
1016 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
1017 Ok(Release(ob.extract()?))
1018 }
1019}
1020
1021impl Release {
1022 pub fn new(paragraph: deb822_lossless::Paragraph) -> Self {
1024 Self(paragraph)
1025 }
1026
1027 pub fn parse(text: &str) -> deb822_lossless::Parse<Release> {
1031 let deb822_parse = deb822_lossless::Deb822::parse(text);
1032 let green = deb822_parse.green().clone();
1033 let mut errors = deb822_parse.errors().to_vec();
1034
1035 if errors.is_empty() {
1037 let deb822 = deb822_parse.tree();
1038 let paragraph_count = deb822.paragraphs().count();
1039 if paragraph_count == 0 {
1040 errors.push("No paragraphs found".to_string());
1041 } else if paragraph_count > 1 {
1042 errors.push("Multiple paragraphs found, expected one".to_string());
1043 }
1044 }
1045
1046 deb822_lossless::Parse::new(green, errors)
1047 }
1048
1049 pub fn origin(&self) -> Option<String> {
1051 self.0.get("Origin").map(|s| s.to_string())
1052 }
1053
1054 pub fn set_origin(&mut self, origin: &str) {
1056 self.0.set("Origin", origin);
1057 }
1058
1059 pub fn label(&self) -> Option<String> {
1061 self.0.get("Label").map(|s| s.to_string())
1062 }
1063
1064 pub fn set_label(&mut self, label: &str) {
1066 self.0.set("Label", label);
1067 }
1068
1069 pub fn suite(&self) -> Option<String> {
1071 self.0.get("Suite").map(|s| s.to_string())
1072 }
1073
1074 pub fn set_suite(&mut self, suite: &str) {
1076 self.0.set("Suite", suite);
1077 }
1078
1079 pub fn codename(&self) -> Option<String> {
1081 self.0.get("Codename").map(|s| s.to_string())
1082 }
1083
1084 pub fn set_codename(&mut self, codename: &str) {
1086 self.0.set("Codename", codename);
1087 }
1088
1089 pub fn changelogs(&self) -> Option<Vec<String>> {
1091 self.0.get("Changelogs").map(|s| {
1092 s.split(',')
1093 .map(|s| s.trim().to_string())
1094 .collect::<Vec<String>>()
1095 })
1096 }
1097
1098 pub fn set_changelogs(&mut self, changelogs: Vec<String>) {
1100 self.0.set("Changelogs", &changelogs.join(", "));
1101 }
1102
1103 #[cfg(feature = "chrono")]
1104 pub fn date(&self) -> Option<chrono::DateTime<chrono::FixedOffset>> {
1106 self.0
1107 .get("Date")
1108 .as_ref()
1109 .map(|s| chrono::DateTime::parse_from_rfc2822(s).unwrap())
1110 }
1111
1112 #[cfg(feature = "chrono")]
1113 pub fn set_date(&mut self, date: chrono::DateTime<chrono::FixedOffset>) {
1115 self.0.set("Date", date.to_rfc2822().as_str());
1116 }
1117
1118 #[cfg(feature = "chrono")]
1119 pub fn valid_until(&self) -> Option<chrono::DateTime<chrono::FixedOffset>> {
1121 self.0
1122 .get("Valid-Until")
1123 .as_ref()
1124 .map(|s| chrono::DateTime::parse_from_rfc2822(s).unwrap())
1125 }
1126
1127 #[cfg(feature = "chrono")]
1128 pub fn set_valid_until(&mut self, date: chrono::DateTime<chrono::FixedOffset>) {
1130 self.0.set("Valid-Until", date.to_rfc2822().as_str());
1131 }
1132
1133 pub fn acquire_by_hash(&self) -> bool {
1135 self.0
1136 .get("Acquire-By-Hash")
1137 .map(|s| s == "yes")
1138 .unwrap_or(false)
1139 }
1140
1141 pub fn set_acquire_by_hash(&mut self, acquire_by_hash: bool) {
1143 self.0.set(
1144 "Acquire-By-Hash",
1145 if acquire_by_hash { "yes" } else { "no" },
1146 );
1147 }
1148
1149 pub fn no_support_for_architecture_all(&self) -> bool {
1151 self.0
1152 .get("No-Support-For-Architecture-All")
1153 .map(|s| s == "yes")
1154 .unwrap_or(false)
1155 }
1156
1157 pub fn set_no_support_for_architecture_all(&mut self, no_support_for_architecture_all: bool) {
1159 self.0.set(
1160 "No-Support-For-Architecture-All",
1161 if no_support_for_architecture_all {
1162 "yes"
1163 } else {
1164 "no"
1165 },
1166 );
1167 }
1168
1169 pub fn architectures(&self) -> Option<Vec<String>> {
1171 self.0.get("Architectures").map(|s| {
1172 s.split_whitespace()
1173 .map(|s| s.trim().to_string())
1174 .collect::<Vec<String>>()
1175 })
1176 }
1177
1178 pub fn set_architectures(&mut self, architectures: Vec<String>) {
1180 self.0.set("Architectures", &architectures.join(" "));
1181 }
1182
1183 pub fn components(&self) -> Option<Vec<String>> {
1185 self.0.get("Components").map(|s| {
1186 s.split_whitespace()
1187 .map(|s| s.trim().to_string())
1188 .collect::<Vec<String>>()
1189 })
1190 }
1191
1192 pub fn set_components(&mut self, components: Vec<String>) {
1194 self.0.set("Components", &components.join(" "));
1195 }
1196
1197 pub fn description(&self) -> Option<String> {
1199 self.0.get_multiline("Description")
1200 }
1201
1202 pub fn set_description(&mut self, description: &str) {
1204 self.0.set("Description", description);
1205 }
1206
1207 pub fn checksums_md5(&self) -> Vec<Md5Checksum> {
1209 self.0
1210 .get("MD5Sum")
1211 .map(|s| {
1212 s.lines()
1213 .map(|line| line.parse().unwrap())
1214 .collect::<Vec<Md5Checksum>>()
1215 })
1216 .unwrap_or_default()
1217 }
1218
1219 pub fn set_checksums_md5(&mut self, files: Vec<Md5Checksum>) {
1221 self.0.set(
1222 "MD5Sum",
1223 &files
1224 .iter()
1225 .map(|f| f.to_string())
1226 .collect::<Vec<String>>()
1227 .join("\n"),
1228 );
1229 }
1230
1231 pub fn checksums_sha1(&self) -> Vec<Sha1Checksum> {
1233 self.0
1234 .get("SHA1")
1235 .map(|s| {
1236 s.lines()
1237 .map(|line| line.parse().unwrap())
1238 .collect::<Vec<Sha1Checksum>>()
1239 })
1240 .unwrap_or_default()
1241 }
1242
1243 pub fn set_checksums_sha1(&mut self, checksums: Vec<Sha1Checksum>) {
1245 self.0.set(
1246 "SHA1",
1247 &checksums
1248 .iter()
1249 .map(|c| c.to_string())
1250 .collect::<Vec<String>>()
1251 .join("\n"),
1252 );
1253 }
1254
1255 pub fn checksums_sha256(&self) -> Vec<Sha256Checksum> {
1257 self.0
1258 .get("SHA256")
1259 .map(|s| {
1260 s.lines()
1261 .map(|line| line.parse().unwrap())
1262 .collect::<Vec<Sha256Checksum>>()
1263 })
1264 .unwrap_or_default()
1265 }
1266
1267 pub fn set_checksums_sha256(&mut self, checksums: Vec<Sha256Checksum>) {
1269 self.0.set(
1270 "SHA256",
1271 &checksums
1272 .iter()
1273 .map(|c| c.to_string())
1274 .collect::<Vec<String>>()
1275 .join("\n"),
1276 );
1277 }
1278
1279 pub fn checksums_sha512(&self) -> Vec<Sha512Checksum> {
1281 self.0
1282 .get("SHA512")
1283 .map(|s| {
1284 s.lines()
1285 .map(|line| line.parse().unwrap())
1286 .collect::<Vec<Sha512Checksum>>()
1287 })
1288 .unwrap_or_default()
1289 }
1290
1291 pub fn set_checksums_sha512(&mut self, checksums: Vec<Sha512Checksum>) {
1293 self.0.set(
1294 "SHA512",
1295 &checksums
1296 .iter()
1297 .map(|c| c.to_string())
1298 .collect::<Vec<String>>()
1299 .join("\n"),
1300 );
1301 }
1302
1303 pub fn not_automatic(&self) -> Option<bool> {
1305 self.0
1306 .get("NotAutomatic")
1307 .map(|s| s.to_lowercase() == "yes")
1308 }
1309
1310 pub fn set_not_automatic(&mut self, v: bool) {
1312 self.0.set("NotAutomatic", if v { "yes" } else { "no" });
1313 }
1314
1315 pub fn but_automatic_upgrades(&self) -> Option<bool> {
1317 self.0
1318 .get("ButAutomaticUpgrades")
1319 .map(|s| s.to_lowercase() == "yes")
1320 }
1321
1322 pub fn set_but_automatic_upgrades(&mut self, v: bool) {
1324 self.0
1325 .set("ButAutomaticUpgrades", if v { "yes" } else { "no" });
1326 }
1327
1328 pub fn version(&self) -> Option<String> {
1330 self.0.get("Version").map(|s| s.to_string())
1331 }
1332
1333 pub fn set_version(&mut self, version: &str) {
1335 self.0.set("Version", version);
1336 }
1337}
1338
1339impl Default for Release {
1340 fn default() -> Self {
1341 Self(deb822_lossless::Paragraph::new())
1342 }
1343}
1344
1345impl std::fmt::Display for Release {
1346 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1347 write!(f, "{}", self.0)
1348 }
1349}
1350
1351impl std::str::FromStr for Release {
1352 type Err = deb822_lossless::ParseError;
1353
1354 fn from_str(s: &str) -> Result<Self, Self::Err> {
1355 Release::parse(s).to_result()
1356 }
1357}
1358
1359impl AstNode for Release {
1360 type Language = deb822_lossless::Lang;
1361
1362 fn can_cast(kind: <Self::Language as rowan::Language>::Kind) -> bool {
1363 deb822_lossless::Paragraph::can_cast(kind) || deb822_lossless::Deb822::can_cast(kind)
1364 }
1365
1366 fn cast(syntax: rowan::SyntaxNode<Self::Language>) -> Option<Self> {
1367 if let Some(para) = deb822_lossless::Paragraph::cast(syntax.clone()) {
1368 Some(Release(para))
1369 } else if let Some(deb822) = deb822_lossless::Deb822::cast(syntax) {
1370 deb822.paragraphs().next().map(Release)
1371 } else {
1372 None
1373 }
1374 }
1375
1376 fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
1377 self.0.syntax()
1378 }
1379}
1380
1381#[cfg(test)]
1382mod tests {
1383 use super::*;
1384 use crate::fields::PackageListEntry;
1385
1386 #[test]
1387 fn test_parse_package_list() {
1388 let s = "package1 binary section standard extra1=foo extra2=bar";
1389 let p: PackageListEntry = s.parse().unwrap();
1390 assert_eq!(p.package, "package1");
1391 assert_eq!(p.package_type, "binary");
1392 assert_eq!(p.section, "section");
1393 assert_eq!(p.priority, super::Priority::Standard);
1394 assert_eq!(p.extra.get("extra1"), Some(&"foo".to_string()));
1395 assert_eq!(p.extra.get("extra2"), Some(&"bar".to_string()));
1396 }
1397
1398 #[test]
1399 fn test_parse_package_list_no_extra() {
1400 let s = "package1 binary section standard";
1401 let p: PackageListEntry = s.parse().unwrap();
1402 assert_eq!(p.package, "package1");
1403 assert_eq!(p.package_type, "binary");
1404 assert_eq!(p.section, "section");
1405 assert_eq!(p.priority, super::Priority::Standard);
1406 assert!(p.extra.is_empty());
1407 }
1408
1409 #[test]
1410 fn test_files() {
1411 let s = "md5sum 1234 filename";
1412 let f: super::Md5Checksum = s.parse().unwrap();
1413 assert_eq!(f.md5sum, "md5sum");
1414 assert_eq!(f.size, 1234);
1415 assert_eq!(f.filename, "filename");
1416 }
1417
1418 #[test]
1419 fn test_sha1_checksum() {
1420 let s = "sha1 1234 filename";
1421 let f: super::Sha1Checksum = s.parse().unwrap();
1422 assert_eq!(f.sha1, "sha1");
1423 assert_eq!(f.size, 1234);
1424 assert_eq!(f.filename, "filename");
1425 }
1426
1427 #[test]
1428 fn test_sha256_checksum() {
1429 let s = "sha256 1234 filename";
1430 let f: super::Sha256Checksum = s.parse().unwrap();
1431 assert_eq!(f.sha256, "sha256");
1432 assert_eq!(f.size, 1234);
1433 assert_eq!(f.filename, "filename");
1434 }
1435
1436 #[test]
1437 fn test_sha512_checksum() {
1438 let s = "sha512 1234 filename";
1439 let f: super::Sha512Checksum = s.parse().unwrap();
1440 assert_eq!(f.sha512, "sha512");
1441 assert_eq!(f.size, 1234);
1442 assert_eq!(f.filename, "filename");
1443 }
1444
1445 #[test]
1446 fn test_source() {
1447 let s = r#"Package: foo
1448Version: 1.0
1449Maintainer: John Doe <john@example.com>
1450Uploaders: Jane Doe <jane@example.com>
1451Standards-Version: 3.9.8
1452Format: 3.0 (quilt)
1453Vcs-Browser: https://example.com/foo
1454Vcs-Git: https://example.com/foo.git
1455Build-Depends: debhelper (>= 9)
1456Build-Depends-Indep: python
1457Build-Depends-Arch: gcc
1458Build-Conflicts: bar
1459Build-Conflicts-Indep: python
1460Build-Conflicts-Arch: gcc
1461Binary: foo, bar
1462Homepage: https://example.com/foo
1463Section: devel
1464Priority: optional
1465Architecture: any
1466Directory: pool/main/f/foo
1467Files:
1468 25dcf3b4b6b3b3b3b3b3b3b3b3b3b3b3 1234 foo_1.0.tar.gz
1469Checksums-Sha1:
1470 b72b5fae3b3b3b3b3b3b3b3b3b3b3b3 1234 foo_1.0.tar.gz
1471"#;
1472 let p: super::Source = s.parse().unwrap();
1473 assert_eq!(p.package(), Some("foo".to_string()));
1474 assert_eq!(p.version(), Some("1.0".parse().unwrap()));
1475 assert_eq!(
1476 p.maintainer(),
1477 Some("John Doe <john@example.com>".to_string())
1478 );
1479 assert_eq!(
1480 p.uploaders(),
1481 Some(vec!["Jane Doe <jane@example.com>".to_string()])
1482 );
1483 assert_eq!(p.standards_version(), Some("3.9.8".to_string()));
1484 assert_eq!(p.format(), Some("3.0 (quilt)".to_string()));
1485 assert_eq!(p.vcs_browser(), Some("https://example.com/foo".to_string()));
1486 assert_eq!(p.vcs_git(), Some("https://example.com/foo.git".to_string()));
1487 assert_eq!(
1488 p.build_depends_indep().map(|x| x.to_string()),
1489 Some("python".parse().unwrap())
1490 );
1491 assert_eq!(p.build_depends(), Some("debhelper (>= 9)".parse().unwrap()));
1492 assert_eq!(p.build_depends_arch(), Some("gcc".parse().unwrap()));
1493 assert_eq!(p.build_conflicts(), Some("bar".parse().unwrap()));
1494 assert_eq!(p.build_conflicts_indep(), Some("python".parse().unwrap()));
1495 assert_eq!(p.build_conflicts_arch(), Some("gcc".parse().unwrap()));
1496 assert_eq!(p.binary(), Some("foo, bar".parse().unwrap()));
1497 assert_eq!(p.homepage(), Some("https://example.com/foo".to_string()));
1498 assert_eq!(p.section(), Some("devel".to_string()));
1499 assert_eq!(p.priority(), Some(super::Priority::Optional));
1500 assert_eq!(p.architecture(), Some("any".to_string()));
1501 assert_eq!(p.directory(), Some("pool/main/f/foo".to_string()));
1502 assert_eq!(p.files().len(), 1);
1503 assert_eq!(
1504 p.files()[0].md5sum,
1505 "25dcf3b4b6b3b3b3b3b3b3b3b3b3b3b3".to_string()
1506 );
1507 assert_eq!(p.files()[0].size, 1234);
1508 assert_eq!(p.files()[0].filename, "foo_1.0.tar.gz".to_string());
1509 assert_eq!(p.checksums_sha1().len(), 1);
1510 assert_eq!(
1511 p.checksums_sha1()[0].sha1,
1512 "b72b5fae3b3b3b3b3b3b3b3b3b3b3b3".to_string()
1513 );
1514 }
1515
1516 #[test]
1517 fn test_package() {
1518 let s = r#"Package: foo
1519Version: 1.0
1520Source: bar
1521Maintainer: John Doe <john@example.com>
1522Architecture: any
1523Depends: bar
1524Recommends: baz
1525Suggests: qux
1526Enhances: quux
1527Pre-Depends: quuz
1528Breaks: corge
1529Conflicts: grault
1530Replaces: garply
1531Provides: waldo
1532Section: devel
1533Priority: optional
1534Description: Foo is a bar
1535Homepage: https://example.com/foo
1536Description-md5: 1234
1537Tags: foo, bar
1538Filename: pool/main/f/foo/foo_1.0.deb
1539Size: 1234
1540Installed-Size: 1234
1541MD5sum: 1234
1542SHA256: 1234
1543Multi-Arch: same
1544"#;
1545 let p: super::Package = s.parse().unwrap();
1546 assert_eq!(p.name(), Some("foo".to_string()));
1547 assert_eq!(p.version(), Some("1.0".parse().unwrap()));
1548 assert_eq!(p.source(), Some("bar".to_string()));
1549 assert_eq!(
1550 p.maintainer(),
1551 Some("John Doe <john@example.com>".to_string())
1552 );
1553 assert_eq!(p.architecture(), Some("any".to_string()));
1554 assert_eq!(p.depends(), Some("bar".parse().unwrap()));
1555 assert_eq!(p.recommends(), Some("baz".parse().unwrap()));
1556 assert_eq!(p.suggests(), Some("qux".parse().unwrap()));
1557 assert_eq!(p.enhances(), Some("quux".parse().unwrap()));
1558 assert_eq!(p.pre_depends(), Some("quuz".parse().unwrap()));
1559 assert_eq!(p.breaks(), Some("corge".parse().unwrap()));
1560 assert_eq!(p.conflicts(), Some("grault".parse().unwrap()));
1561 assert_eq!(p.replaces(), Some("garply".parse().unwrap()));
1562 assert_eq!(p.provides(), Some("waldo".parse().unwrap()));
1563 assert_eq!(p.section(), Some("devel".to_string()));
1564 assert_eq!(p.priority(), Some(super::Priority::Optional));
1565 assert_eq!(p.description(), Some("Foo is a bar".to_string()));
1566 assert_eq!(
1567 p.homepage(),
1568 Some(url::Url::parse("https://example.com/foo").unwrap())
1569 );
1570 assert_eq!(p.description_md5(), Some("1234".to_string()));
1571 assert_eq!(
1572 p.tags("Tags"),
1573 Some(vec!["foo".to_string(), "bar".to_string()])
1574 );
1575 assert_eq!(
1576 p.filename(),
1577 Some("pool/main/f/foo/foo_1.0.deb".to_string())
1578 );
1579 assert_eq!(p.size(), Some(1234));
1580 assert_eq!(p.installed_size(), Some(1234));
1581 assert_eq!(p.md5sum(), Some("1234".to_string()));
1582 assert_eq!(p.sha256(), Some("1234".to_string()));
1583 assert_eq!(p.multi_arch(), Some(MultiArch::Same));
1584 }
1585
1586 #[test]
1587 fn test_release() {
1588 let s = include_str!("../testdata/Release");
1589 let release: super::Release = s.parse().unwrap();
1590
1591 assert_eq!(release.origin(), Some("Debian".to_string()));
1592 assert_eq!(release.label(), Some("Debian".to_string()));
1593 assert_eq!(release.suite(), Some("testing".to_string()));
1594 assert_eq!(
1595 release.architectures(),
1596 vec![
1597 "all".to_string(),
1598 "amd64".to_string(),
1599 "arm64".to_string(),
1600 "armel".to_string(),
1601 "armhf".to_string()
1602 ]
1603 .into()
1604 );
1605 assert_eq!(
1606 release.components(),
1607 vec![
1608 "main".to_string(),
1609 "contrib".to_string(),
1610 "non-free-firmware".to_string(),
1611 "non-free".to_string()
1612 ]
1613 .into()
1614 );
1615 assert_eq!(
1616 release.description(),
1617 Some("Debian x.y Testing distribution - Not Released".to_string())
1618 );
1619 assert_eq!(318, release.checksums_md5().len());
1620 }
1621
1622 #[test]
1623 fn test_source_vcs_arch() {
1624 let s = r#"Package: foo
1625Version: 1.0
1626Vcs-Arch: https://example.com/arch/repo
1627"#;
1628 let p: super::Source = s.parse().unwrap();
1629 assert_eq!(
1630 p.vcs_arch(),
1631 Some("https://example.com/arch/repo".to_string())
1632 );
1633 }
1634
1635 #[test]
1636 fn test_source_vcs_cvs() {
1637 let s = r#"Package: foo
1638Version: 1.0
1639Vcs-Cvs: :pserver:anoncvs@example.com:/cvs/repo
1640"#;
1641 let p: super::Source = s.parse().unwrap();
1642 assert_eq!(
1643 p.vcs_cvs(),
1644 Some(":pserver:anoncvs@example.com:/cvs/repo".to_string())
1645 );
1646 }
1647
1648 #[test]
1649 fn test_source_set_priority() {
1650 let s = r#"Package: foo
1651Version: 1.0
1652Priority: optional
1653"#;
1654 let mut p: super::Source = s.parse().unwrap();
1655 p.set_priority(super::Priority::Required);
1656 assert_eq!(p.priority(), Some(super::Priority::Required));
1657 }
1658
1659 #[test]
1660 fn test_release_set_suite() {
1661 let s = r#"Origin: Debian
1662Label: Debian
1663Suite: testing
1664"#;
1665 let mut release: super::Release = s.parse().unwrap();
1666 release.set_suite("unstable");
1667 assert_eq!(release.suite(), Some("unstable".to_string()));
1668 }
1669
1670 #[test]
1671 fn test_release_codename() {
1672 let s = r#"Origin: Debian
1673Label: Debian
1674Suite: testing
1675Codename: trixie
1676"#;
1677 let release: super::Release = s.parse().unwrap();
1678 assert_eq!(release.codename(), Some("trixie".to_string()));
1679 }
1680
1681 #[test]
1682 fn test_release_set_checksums_sha512() {
1683 let s = r#"Origin: Debian
1684Label: Debian
1685Suite: testing
1686"#;
1687 let mut release: super::Release = s.parse().unwrap();
1688 let checksums = vec![super::Sha512Checksum {
1689 sha512: "abc123".to_string(),
1690 size: 1234,
1691 filename: "test.deb".to_string(),
1692 }];
1693 release.set_checksums_sha512(checksums);
1694 assert_eq!(release.checksums_sha512().len(), 1);
1695 }
1696
1697 #[test]
1698 fn test_package_set_replaces() {
1699 let s = r#"Package: foo
1700Version: 1.0
1701"#;
1702 let mut p: super::Package = s.parse().unwrap();
1703 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1704 p.set_replaces(relations);
1705 assert_eq!(p.replaces(), Some("bar (>= 1.0.0)".parse().unwrap()));
1706 }
1707}