1use crate::fields::{MultiArch, Priority};
36use crate::lossless::relations::Relations;
37use deb822_lossless::{Deb822, Paragraph, BINARY_FIELD_ORDER, SOURCE_FIELD_ORDER};
38use rowan::ast::AstNode;
39
40fn format_field(name: &str, value: &str) -> String {
41 match name {
42 "Uploaders" => value
43 .split(',')
44 .map(|s| s.trim().to_string())
45 .collect::<Vec<_>>()
46 .join(",\n"),
47 "Build-Depends"
48 | "Build-Depends-Indep"
49 | "Build-Depends-Arch"
50 | "Build-Conflicts"
51 | "Build-Conflicts-Indep"
52 | "Build-Conflics-Arch"
53 | "Depends"
54 | "Recommends"
55 | "Suggests"
56 | "Enhances"
57 | "Pre-Depends"
58 | "Breaks" => {
59 let relations: Relations = value.parse().unwrap();
60 let relations = relations.wrap_and_sort();
61 relations.to_string()
62 }
63 _ => value.to_string(),
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct Control(Deb822);
70
71impl Control {
72 pub fn new() -> Self {
74 Control(Deb822::new())
75 }
76
77 pub fn as_mut_deb822(&mut self) -> &mut Deb822 {
79 &mut self.0
80 }
81
82 pub fn as_deb822(&self) -> &Deb822 {
84 &self.0
85 }
86
87 pub fn parse(text: &str) -> deb822_lossless::Parse<Control> {
89 let deb822_parse = Deb822::parse(text);
90 let green = deb822_parse.green().clone();
92 let errors = deb822_parse.errors().to_vec();
93 let positioned_errors = deb822_parse.positioned_errors().to_vec();
94 deb822_lossless::Parse::new_with_positioned_errors(green, errors, positioned_errors)
95 }
96
97 pub fn source(&self) -> Option<Source> {
99 self.0
100 .paragraphs()
101 .find(|p| p.get("Source").is_some())
102 .map(Source)
103 }
104
105 pub fn binaries(&self) -> impl Iterator<Item = Binary> {
107 self.0
108 .paragraphs()
109 .filter(|p| p.get("Package").is_some())
110 .map(Binary)
111 }
112
113 pub fn add_source(&mut self, name: &str) -> Source {
129 let mut p = self.0.add_paragraph();
130 p.set("Source", name);
131 self.source().unwrap()
132 }
133
134 pub fn add_binary(&mut self, name: &str) -> Binary {
150 let mut p = self.0.add_paragraph();
151 p.set("Package", name);
152 Binary(p)
153 }
154
155 pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self, deb822_lossless::Error> {
157 Ok(Control(Deb822::from_file(path)?))
158 }
159
160 pub fn from_file_relaxed<P: AsRef<std::path::Path>>(
162 path: P,
163 ) -> Result<(Self, Vec<String>), std::io::Error> {
164 let (control, errors) = Deb822::from_file_relaxed(path)?;
165 Ok((Control(control), errors))
166 }
167
168 pub fn read<R: std::io::Read>(mut r: R) -> Result<Self, deb822_lossless::Error> {
170 Ok(Control(Deb822::read(&mut r)?))
171 }
172
173 pub fn read_relaxed<R: std::io::Read>(
175 mut r: R,
176 ) -> Result<(Self, Vec<String>), deb822_lossless::Error> {
177 let (control, errors) = Deb822::read_relaxed(&mut r)?;
178 Ok((Self(control), errors))
179 }
180
181 pub fn wrap_and_sort(
188 &mut self,
189 indentation: deb822_lossless::Indentation,
190 immediate_empty_line: bool,
191 max_line_length_one_liner: Option<usize>,
192 ) {
193 let sort_paragraphs = |a: &Paragraph, b: &Paragraph| -> std::cmp::Ordering {
194 let a_is_source = a.get("Source").is_some();
196 let b_is_source = b.get("Source").is_some();
197
198 if a_is_source && !b_is_source {
199 return std::cmp::Ordering::Less;
200 } else if !a_is_source && b_is_source {
201 return std::cmp::Ordering::Greater;
202 } else if a_is_source && b_is_source {
203 return a.get("Source").cmp(&b.get("Source"));
204 }
205
206 a.get("Package").cmp(&b.get("Package"))
207 };
208
209 let wrap_paragraph = |p: &Paragraph| -> Paragraph {
210 p.wrap_and_sort(
213 indentation,
214 immediate_empty_line,
215 max_line_length_one_liner,
216 None,
217 Some(&format_field),
218 )
219 };
220
221 self.0 = self
222 .0
223 .wrap_and_sort(Some(&sort_paragraphs), Some(&wrap_paragraph));
224 }
225
226 pub fn sort_binaries(&mut self, keep_first: bool) {
257 let mut paragraphs: Vec<_> = self.0.paragraphs().collect();
258
259 if paragraphs.len() <= 1 {
260 return; }
262
263 let source_idx = paragraphs.iter().position(|p| p.get("Source").is_some());
265 let binary_start = source_idx.map(|i| i + 1).unwrap_or(0);
266
267 let sort_start = if keep_first && paragraphs.len() > binary_start + 1 {
269 binary_start + 1
270 } else {
271 binary_start
272 };
273
274 if sort_start >= paragraphs.len() {
275 return; }
277
278 paragraphs[sort_start..].sort_by(|a, b| {
280 let a_name = a.get("Package");
281 let b_name = b.get("Package");
282 a_name.cmp(&b_name)
283 });
284
285 let sort_paragraphs = |a: &Paragraph, b: &Paragraph| -> std::cmp::Ordering {
287 let a_pos = paragraphs.iter().position(|p| p == a);
288 let b_pos = paragraphs.iter().position(|p| p == b);
289 a_pos.cmp(&b_pos)
290 };
291
292 self.0 = self.0.wrap_and_sort(Some(&sort_paragraphs), None);
293 }
294
295 pub fn fields_in_range(
324 &self,
325 range: rowan::TextRange,
326 ) -> impl Iterator<Item = deb822_lossless::Entry> + '_ {
327 self.0
328 .paragraphs()
329 .flat_map(move |p| p.entries().collect::<Vec<_>>())
330 .filter(move |entry| {
331 let entry_range = entry.syntax().text_range();
332 entry_range.start() < range.end() && range.start() < entry_range.end()
334 })
335 }
336}
337
338impl From<Control> for Deb822 {
339 fn from(c: Control) -> Self {
340 c.0
341 }
342}
343
344impl From<Deb822> for Control {
345 fn from(d: Deb822) -> Self {
346 Control(d)
347 }
348}
349
350impl Default for Control {
351 fn default() -> Self {
352 Self::new()
353 }
354}
355
356impl std::str::FromStr for Control {
357 type Err = deb822_lossless::ParseError;
358
359 fn from_str(s: &str) -> Result<Self, Self::Err> {
360 Control::parse(s).to_result()
361 }
362}
363
364#[derive(Debug, Clone, PartialEq, Eq)]
366pub struct Source(Paragraph);
367
368impl From<Source> for Paragraph {
369 fn from(s: Source) -> Self {
370 s.0
371 }
372}
373
374impl From<Paragraph> for Source {
375 fn from(p: Paragraph) -> Self {
376 Source(p)
377 }
378}
379
380impl std::fmt::Display for Source {
381 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
382 self.0.fmt(f)
383 }
384}
385
386impl Source {
387 pub fn name(&self) -> Option<String> {
389 self.0.get("Source")
390 }
391
392 pub fn wrap_and_sort(
394 &mut self,
395 indentation: deb822_lossless::Indentation,
396 immediate_empty_line: bool,
397 max_line_length_one_liner: Option<usize>,
398 ) {
399 self.0 = self.0.wrap_and_sort(
400 indentation,
401 immediate_empty_line,
402 max_line_length_one_liner,
403 None,
404 Some(&format_field),
405 );
406 }
407
408 pub fn as_mut_deb822(&mut self) -> &mut Paragraph {
410 &mut self.0
411 }
412
413 pub fn as_deb822(&self) -> &Paragraph {
415 &self.0
416 }
417
418 pub fn set_name(&mut self, name: &str) {
420 self.set("Source", name);
421 }
422
423 pub fn section(&self) -> Option<String> {
425 self.0.get("Section")
426 }
427
428 pub fn set_section(&mut self, section: Option<&str>) {
430 if let Some(section) = section {
431 self.set("Section", section);
432 } else {
433 self.0.remove("Section");
434 }
435 }
436
437 pub fn priority(&self) -> Option<Priority> {
439 self.0.get("Priority").and_then(|v| v.parse().ok())
440 }
441
442 pub fn set_priority(&mut self, priority: Option<Priority>) {
444 if let Some(priority) = priority {
445 self.set("Priority", priority.to_string().as_str());
446 } else {
447 self.0.remove("Priority");
448 }
449 }
450
451 pub fn maintainer(&self) -> Option<String> {
453 self.0.get("Maintainer")
454 }
455
456 pub fn set_maintainer(&mut self, maintainer: &str) {
458 self.set("Maintainer", maintainer);
459 }
460
461 pub fn build_depends(&self) -> Option<Relations> {
463 self.0.get("Build-Depends").map(|s| s.parse().unwrap())
464 }
465
466 pub fn set_build_depends(&mut self, relations: &Relations) {
468 self.set("Build-Depends", relations.to_string().as_str());
469 }
470
471 pub fn build_depends_indep(&self) -> Option<Relations> {
473 self.0
474 .get("Build-Depends-Indep")
475 .map(|s| s.parse().unwrap())
476 }
477
478 pub fn build_depends_arch(&self) -> Option<Relations> {
480 self.0.get("Build-Depends-Arch").map(|s| s.parse().unwrap())
481 }
482
483 pub fn build_conflicts(&self) -> Option<Relations> {
485 self.0.get("Build-Conflicts").map(|s| s.parse().unwrap())
486 }
487
488 pub fn build_conflicts_indep(&self) -> Option<Relations> {
490 self.0
491 .get("Build-Conflicts-Indep")
492 .map(|s| s.parse().unwrap())
493 }
494
495 pub fn build_conflicts_arch(&self) -> Option<Relations> {
497 self.0
498 .get("Build-Conflicts-Arch")
499 .map(|s| s.parse().unwrap())
500 }
501
502 pub fn standards_version(&self) -> Option<String> {
504 self.0.get("Standards-Version")
505 }
506
507 pub fn set_standards_version(&mut self, version: &str) {
509 self.set("Standards-Version", version);
510 }
511
512 pub fn homepage(&self) -> Option<url::Url> {
514 self.0.get("Homepage").and_then(|s| s.parse().ok())
515 }
516
517 pub fn set_homepage(&mut self, homepage: &url::Url) {
519 self.set("Homepage", homepage.to_string().as_str());
520 }
521
522 pub fn vcs_git(&self) -> Option<String> {
524 self.0.get("Vcs-Git")
525 }
526
527 pub fn set_vcs_git(&mut self, url: &str) {
529 self.set("Vcs-Git", url);
530 }
531
532 pub fn vcs_svn(&self) -> Option<String> {
534 self.0.get("Vcs-Svn").map(|s| s.to_string())
535 }
536
537 pub fn set_vcs_svn(&mut self, url: &str) {
539 self.set("Vcs-Svn", url);
540 }
541
542 pub fn vcs_bzr(&self) -> Option<String> {
544 self.0.get("Vcs-Bzr").map(|s| s.to_string())
545 }
546
547 pub fn set_vcs_bzr(&mut self, url: &str) {
549 self.set("Vcs-Bzr", url);
550 }
551
552 pub fn vcs_arch(&self) -> Option<String> {
554 self.0.get("Vcs-Arch").map(|s| s.to_string())
555 }
556
557 pub fn set_vcs_arch(&mut self, url: &str) {
559 self.set("Vcs-Arch", url);
560 }
561
562 pub fn vcs_svk(&self) -> Option<String> {
564 self.0.get("Vcs-Svk").map(|s| s.to_string())
565 }
566
567 pub fn set_vcs_svk(&mut self, url: &str) {
569 self.set("Vcs-Svk", url);
570 }
571
572 pub fn vcs_darcs(&self) -> Option<String> {
574 self.0.get("Vcs-Darcs").map(|s| s.to_string())
575 }
576
577 pub fn set_vcs_darcs(&mut self, url: &str) {
579 self.set("Vcs-Darcs", url);
580 }
581
582 pub fn vcs_mtn(&self) -> Option<String> {
584 self.0.get("Vcs-Mtn").map(|s| s.to_string())
585 }
586
587 pub fn set_vcs_mtn(&mut self, url: &str) {
589 self.set("Vcs-Mtn", url);
590 }
591
592 pub fn vcs_cvs(&self) -> Option<String> {
594 self.0.get("Vcs-Cvs").map(|s| s.to_string())
595 }
596
597 pub fn set_vcs_cvs(&mut self, url: &str) {
599 self.set("Vcs-Cvs", url);
600 }
601
602 pub fn vcs_hg(&self) -> Option<String> {
604 self.0.get("Vcs-Hg").map(|s| s.to_string())
605 }
606
607 pub fn set_vcs_hg(&mut self, url: &str) {
609 self.set("Vcs-Hg", url);
610 }
611
612 pub fn set(&mut self, key: &str, value: &str) {
614 self.0.set_with_field_order(key, value, SOURCE_FIELD_ORDER);
615 }
616
617 pub fn vcs_browser(&self) -> Option<String> {
619 self.0.get("Vcs-Browser")
620 }
621
622 pub fn vcs(&self) -> Option<crate::vcs::Vcs> {
624 for (name, value) in self.0.items() {
625 if name.starts_with("Vcs-") && name != "Vcs-Browser" {
626 return crate::vcs::Vcs::from_field(&name, &value).ok();
627 }
628 }
629 None
630 }
631
632 pub fn set_vcs_browser(&mut self, url: Option<&str>) {
634 if let Some(url) = url {
635 self.set("Vcs-Browser", url);
636 } else {
637 self.0.remove("Vcs-Browser");
638 }
639 }
640
641 pub fn uploaders(&self) -> Option<Vec<String>> {
643 self.0
644 .get("Uploaders")
645 .map(|s| s.split(',').map(|s| s.trim().to_owned()).collect())
646 }
647
648 pub fn set_uploaders(&mut self, uploaders: &[&str]) {
650 self.set(
651 "Uploaders",
652 uploaders
653 .iter()
654 .map(|s| s.to_string())
655 .collect::<Vec<_>>()
656 .join(", ")
657 .as_str(),
658 );
659 }
660
661 pub fn architecture(&self) -> Option<String> {
663 self.0.get("Architecture")
664 }
665
666 pub fn set_architecture(&mut self, arch: Option<&str>) {
668 if let Some(arch) = arch {
669 self.set("Architecture", arch);
670 } else {
671 self.0.remove("Architecture");
672 }
673 }
674
675 pub fn rules_requires_root(&self) -> Option<bool> {
677 self.0
678 .get("Rules-Requires-Root")
679 .map(|s| match s.to_lowercase().as_str() {
680 "yes" => true,
681 "no" => false,
682 _ => panic!("invalid Rules-Requires-Root value"),
683 })
684 }
685
686 pub fn set_rules_requires_root(&mut self, requires_root: bool) {
688 self.set(
689 "Rules-Requires-Root",
690 if requires_root { "yes" } else { "no" },
691 );
692 }
693
694 pub fn testsuite(&self) -> Option<String> {
696 self.0.get("Testsuite")
697 }
698
699 pub fn set_testsuite(&mut self, testsuite: &str) {
701 self.set("Testsuite", testsuite);
702 }
703
704 pub fn overlaps_range(&self, range: rowan::TextRange) -> bool {
712 let para_range = self.0.syntax().text_range();
713 para_range.start() < range.end() && range.start() < para_range.end()
714 }
715
716 pub fn fields_in_range(
724 &self,
725 range: rowan::TextRange,
726 ) -> impl Iterator<Item = deb822_lossless::Entry> + '_ {
727 self.0.entries().filter(move |entry| {
728 let entry_range = entry.syntax().text_range();
729 entry_range.start() < range.end() && range.start() < entry_range.end()
730 })
731 }
732}
733
734#[cfg(feature = "python-debian")]
735impl<'py> pyo3::IntoPyObject<'py> for Source {
736 type Target = pyo3::PyAny;
737 type Output = pyo3::Bound<'py, Self::Target>;
738 type Error = pyo3::PyErr;
739
740 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
741 self.0.into_pyobject(py)
742 }
743}
744
745#[cfg(feature = "python-debian")]
746impl<'a, 'py> pyo3::IntoPyObject<'py> for &'a Source {
747 type Target = pyo3::PyAny;
748 type Output = pyo3::Bound<'py, Self::Target>;
749 type Error = pyo3::PyErr;
750
751 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
752 (&self.0).into_pyobject(py)
753 }
754}
755
756#[cfg(feature = "python-debian")]
757impl<'py> pyo3::FromPyObject<'_, 'py> for Source {
758 type Error = pyo3::PyErr;
759
760 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
761 Ok(Source(ob.extract()?))
762 }
763}
764
765impl std::fmt::Display for Control {
766 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
767 self.0.fmt(f)
768 }
769}
770
771impl AstNode for Control {
772 type Language = deb822_lossless::Lang;
773
774 fn can_cast(kind: <Self::Language as rowan::Language>::Kind) -> bool {
775 Deb822::can_cast(kind)
776 }
777
778 fn cast(syntax: rowan::SyntaxNode<Self::Language>) -> Option<Self> {
779 Deb822::cast(syntax).map(Control)
780 }
781
782 fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
783 self.0.syntax()
784 }
785}
786
787#[derive(Debug, Clone, PartialEq, Eq)]
789pub struct Binary(Paragraph);
790
791impl From<Binary> for Paragraph {
792 fn from(b: Binary) -> Self {
793 b.0
794 }
795}
796
797impl From<Paragraph> for Binary {
798 fn from(p: Paragraph) -> Self {
799 Binary(p)
800 }
801}
802
803#[cfg(feature = "python-debian")]
804impl<'py> pyo3::IntoPyObject<'py> for Binary {
805 type Target = pyo3::PyAny;
806 type Output = pyo3::Bound<'py, Self::Target>;
807 type Error = pyo3::PyErr;
808
809 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
810 self.0.into_pyobject(py)
811 }
812}
813
814#[cfg(feature = "python-debian")]
815impl<'a, 'py> pyo3::IntoPyObject<'py> for &'a Binary {
816 type Target = pyo3::PyAny;
817 type Output = pyo3::Bound<'py, Self::Target>;
818 type Error = pyo3::PyErr;
819
820 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
821 (&self.0).into_pyobject(py)
822 }
823}
824
825#[cfg(feature = "python-debian")]
826impl<'py> pyo3::FromPyObject<'_, 'py> for Binary {
827 type Error = pyo3::PyErr;
828
829 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
830 Ok(Binary(ob.extract()?))
831 }
832}
833
834impl Default for Binary {
835 fn default() -> Self {
836 Self::new()
837 }
838}
839
840impl std::fmt::Display for Binary {
841 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
842 self.0.fmt(f)
843 }
844}
845
846impl Binary {
847 pub fn new() -> Self {
849 Binary(Paragraph::new())
850 }
851
852 pub fn as_mut_deb822(&mut self) -> &mut Paragraph {
854 &mut self.0
855 }
856
857 pub fn as_deb822(&self) -> &Paragraph {
859 &self.0
860 }
861
862 pub fn wrap_and_sort(
864 &mut self,
865 indentation: deb822_lossless::Indentation,
866 immediate_empty_line: bool,
867 max_line_length_one_liner: Option<usize>,
868 ) {
869 self.0 = self.0.wrap_and_sort(
870 indentation,
871 immediate_empty_line,
872 max_line_length_one_liner,
873 None,
874 Some(&format_field),
875 );
876 }
877
878 pub fn name(&self) -> Option<String> {
880 self.0.get("Package")
881 }
882
883 pub fn set_name(&mut self, name: &str) {
885 self.set("Package", name);
886 }
887
888 pub fn section(&self) -> Option<String> {
890 self.0.get("Section")
891 }
892
893 pub fn set_section(&mut self, section: Option<&str>) {
895 if let Some(section) = section {
896 self.set("Section", section);
897 } else {
898 self.0.remove("Section");
899 }
900 }
901
902 pub fn priority(&self) -> Option<Priority> {
904 self.0.get("Priority").and_then(|v| v.parse().ok())
905 }
906
907 pub fn set_priority(&mut self, priority: Option<Priority>) {
909 if let Some(priority) = priority {
910 self.set("Priority", priority.to_string().as_str());
911 } else {
912 self.0.remove("Priority");
913 }
914 }
915
916 pub fn architecture(&self) -> Option<String> {
918 self.0.get("Architecture")
919 }
920
921 pub fn set_architecture(&mut self, arch: Option<&str>) {
923 if let Some(arch) = arch {
924 self.set("Architecture", arch);
925 } else {
926 self.0.remove("Architecture");
927 }
928 }
929
930 pub fn depends(&self) -> Option<Relations> {
932 self.0.get("Depends").map(|s| s.parse().unwrap())
933 }
934
935 pub fn set_depends(&mut self, depends: Option<&Relations>) {
937 if let Some(depends) = depends {
938 self.set("Depends", depends.to_string().as_str());
939 } else {
940 self.0.remove("Depends");
941 }
942 }
943
944 pub fn recommends(&self) -> Option<Relations> {
946 self.0.get("Recommends").map(|s| s.parse().unwrap())
947 }
948
949 pub fn set_recommends(&mut self, recommends: Option<&Relations>) {
951 if let Some(recommends) = recommends {
952 self.set("Recommends", recommends.to_string().as_str());
953 } else {
954 self.0.remove("Recommends");
955 }
956 }
957
958 pub fn suggests(&self) -> Option<Relations> {
960 self.0.get("Suggests").map(|s| s.parse().unwrap())
961 }
962
963 pub fn set_suggests(&mut self, suggests: Option<&Relations>) {
965 if let Some(suggests) = suggests {
966 self.set("Suggests", suggests.to_string().as_str());
967 } else {
968 self.0.remove("Suggests");
969 }
970 }
971
972 pub fn enhances(&self) -> Option<Relations> {
974 self.0.get("Enhances").map(|s| s.parse().unwrap())
975 }
976
977 pub fn set_enhances(&mut self, enhances: Option<&Relations>) {
979 if let Some(enhances) = enhances {
980 self.set("Enhances", enhances.to_string().as_str());
981 } else {
982 self.0.remove("Enhances");
983 }
984 }
985
986 pub fn pre_depends(&self) -> Option<Relations> {
988 self.0.get("Pre-Depends").map(|s| s.parse().unwrap())
989 }
990
991 pub fn set_pre_depends(&mut self, pre_depends: Option<&Relations>) {
993 if let Some(pre_depends) = pre_depends {
994 self.set("Pre-Depends", pre_depends.to_string().as_str());
995 } else {
996 self.0.remove("Pre-Depends");
997 }
998 }
999
1000 pub fn breaks(&self) -> Option<Relations> {
1002 self.0.get("Breaks").map(|s| s.parse().unwrap())
1003 }
1004
1005 pub fn set_breaks(&mut self, breaks: Option<&Relations>) {
1007 if let Some(breaks) = breaks {
1008 self.set("Breaks", breaks.to_string().as_str());
1009 } else {
1010 self.0.remove("Breaks");
1011 }
1012 }
1013
1014 pub fn conflicts(&self) -> Option<Relations> {
1016 self.0.get("Conflicts").map(|s| s.parse().unwrap())
1017 }
1018
1019 pub fn set_conflicts(&mut self, conflicts: Option<&Relations>) {
1021 if let Some(conflicts) = conflicts {
1022 self.set("Conflicts", conflicts.to_string().as_str());
1023 } else {
1024 self.0.remove("Conflicts");
1025 }
1026 }
1027
1028 pub fn replaces(&self) -> Option<Relations> {
1030 self.0.get("Replaces").map(|s| s.parse().unwrap())
1031 }
1032
1033 pub fn set_replaces(&mut self, replaces: Option<&Relations>) {
1035 if let Some(replaces) = replaces {
1036 self.set("Replaces", replaces.to_string().as_str());
1037 } else {
1038 self.0.remove("Replaces");
1039 }
1040 }
1041
1042 pub fn provides(&self) -> Option<Relations> {
1044 self.0.get("Provides").map(|s| s.parse().unwrap())
1045 }
1046
1047 pub fn set_provides(&mut self, provides: Option<&Relations>) {
1049 if let Some(provides) = provides {
1050 self.set("Provides", provides.to_string().as_str());
1051 } else {
1052 self.0.remove("Provides");
1053 }
1054 }
1055
1056 pub fn built_using(&self) -> Option<Relations> {
1058 self.0.get("Built-Using").map(|s| s.parse().unwrap())
1059 }
1060
1061 pub fn set_built_using(&mut self, built_using: Option<&Relations>) {
1063 if let Some(built_using) = built_using {
1064 self.set("Built-Using", built_using.to_string().as_str());
1065 } else {
1066 self.0.remove("Built-Using");
1067 }
1068 }
1069
1070 pub fn multi_arch(&self) -> Option<MultiArch> {
1072 self.0.get("Multi-Arch").map(|s| s.parse().unwrap())
1073 }
1074
1075 pub fn set_multi_arch(&mut self, multi_arch: Option<MultiArch>) {
1077 if let Some(multi_arch) = multi_arch {
1078 self.set("Multi-Arch", multi_arch.to_string().as_str());
1079 } else {
1080 self.0.remove("Multi-Arch");
1081 }
1082 }
1083
1084 pub fn essential(&self) -> bool {
1086 self.0.get("Essential").map(|s| s == "yes").unwrap_or(false)
1087 }
1088
1089 pub fn set_essential(&mut self, essential: bool) {
1091 if essential {
1092 self.set("Essential", "yes");
1093 } else {
1094 self.0.remove("Essential");
1095 }
1096 }
1097
1098 pub fn description(&self) -> Option<String> {
1100 self.0.get("Description")
1101 }
1102
1103 pub fn set_description(&mut self, description: Option<&str>) {
1105 if let Some(description) = description {
1106 self.set("Description", description);
1107 } else {
1108 self.0.remove("Description");
1109 }
1110 }
1111
1112 pub fn homepage(&self) -> Option<url::Url> {
1114 self.0.get("Homepage").and_then(|s| s.parse().ok())
1115 }
1116
1117 pub fn set_homepage(&mut self, url: &url::Url) {
1119 self.set("Homepage", url.as_str());
1120 }
1121
1122 pub fn set(&mut self, key: &str, value: &str) {
1124 self.0.set_with_field_order(key, value, BINARY_FIELD_ORDER);
1125 }
1126
1127 pub fn overlaps_range(&self, range: rowan::TextRange) -> bool {
1135 let para_range = self.0.syntax().text_range();
1136 para_range.start() < range.end() && range.start() < para_range.end()
1137 }
1138
1139 pub fn fields_in_range(
1147 &self,
1148 range: rowan::TextRange,
1149 ) -> impl Iterator<Item = deb822_lossless::Entry> + '_ {
1150 self.0.entries().filter(move |entry| {
1151 let entry_range = entry.syntax().text_range();
1152 entry_range.start() < range.end() && range.start() < entry_range.end()
1153 })
1154 }
1155}
1156
1157#[cfg(test)]
1158mod tests {
1159 use super::*;
1160 use crate::relations::VersionConstraint;
1161
1162 #[test]
1163 fn test_source_set_field_ordering() {
1164 let mut control = Control::new();
1165 let mut source = control.add_source("mypackage");
1166
1167 source.set("Homepage", "https://example.com");
1169 source.set("Build-Depends", "debhelper");
1170 source.set("Standards-Version", "4.5.0");
1171 source.set("Maintainer", "Test <test@example.com>");
1172
1173 let output = source.to_string();
1175 let lines: Vec<&str> = output.lines().collect();
1176
1177 assert!(lines[0].starts_with("Source:"));
1179
1180 let maintainer_pos = lines
1182 .iter()
1183 .position(|l| l.starts_with("Maintainer:"))
1184 .unwrap();
1185 let build_depends_pos = lines
1186 .iter()
1187 .position(|l| l.starts_with("Build-Depends:"))
1188 .unwrap();
1189 let standards_pos = lines
1190 .iter()
1191 .position(|l| l.starts_with("Standards-Version:"))
1192 .unwrap();
1193 let homepage_pos = lines
1194 .iter()
1195 .position(|l| l.starts_with("Homepage:"))
1196 .unwrap();
1197
1198 assert!(maintainer_pos < build_depends_pos);
1200 assert!(build_depends_pos < standards_pos);
1201 assert!(standards_pos < homepage_pos);
1202 }
1203
1204 #[test]
1205 fn test_binary_set_field_ordering() {
1206 let mut control = Control::new();
1207 let mut binary = control.add_binary("mypackage");
1208
1209 binary.set("Description", "A test package");
1211 binary.set("Architecture", "amd64");
1212 binary.set("Depends", "libc6");
1213 binary.set("Section", "utils");
1214
1215 let output = binary.to_string();
1217 let lines: Vec<&str> = output.lines().collect();
1218
1219 assert!(lines[0].starts_with("Package:"));
1221
1222 let arch_pos = lines
1224 .iter()
1225 .position(|l| l.starts_with("Architecture:"))
1226 .unwrap();
1227 let section_pos = lines
1228 .iter()
1229 .position(|l| l.starts_with("Section:"))
1230 .unwrap();
1231 let depends_pos = lines
1232 .iter()
1233 .position(|l| l.starts_with("Depends:"))
1234 .unwrap();
1235 let desc_pos = lines
1236 .iter()
1237 .position(|l| l.starts_with("Description:"))
1238 .unwrap();
1239
1240 assert!(arch_pos < section_pos);
1242 assert!(section_pos < depends_pos);
1243 assert!(depends_pos < desc_pos);
1244 }
1245
1246 #[test]
1247 fn test_source_specific_set_methods_use_field_ordering() {
1248 let mut control = Control::new();
1249 let mut source = control.add_source("mypackage");
1250
1251 source.set_homepage(&"https://example.com".parse().unwrap());
1253 source.set_maintainer("Test <test@example.com>");
1254 source.set_standards_version("4.5.0");
1255 source.set_vcs_git("https://github.com/example/repo");
1256
1257 let output = source.to_string();
1259 let lines: Vec<&str> = output.lines().collect();
1260
1261 let source_pos = lines.iter().position(|l| l.starts_with("Source:")).unwrap();
1263 let maintainer_pos = lines
1264 .iter()
1265 .position(|l| l.starts_with("Maintainer:"))
1266 .unwrap();
1267 let standards_pos = lines
1268 .iter()
1269 .position(|l| l.starts_with("Standards-Version:"))
1270 .unwrap();
1271 let vcs_git_pos = lines
1272 .iter()
1273 .position(|l| l.starts_with("Vcs-Git:"))
1274 .unwrap();
1275 let homepage_pos = lines
1276 .iter()
1277 .position(|l| l.starts_with("Homepage:"))
1278 .unwrap();
1279
1280 assert!(source_pos < maintainer_pos);
1282 assert!(maintainer_pos < standards_pos);
1283 assert!(standards_pos < vcs_git_pos);
1284 assert!(vcs_git_pos < homepage_pos);
1285 }
1286
1287 #[test]
1288 fn test_binary_specific_set_methods_use_field_ordering() {
1289 let mut control = Control::new();
1290 let mut binary = control.add_binary("mypackage");
1291
1292 binary.set_description(Some("A test package"));
1294 binary.set_architecture(Some("amd64"));
1295 let depends = "libc6".parse().unwrap();
1296 binary.set_depends(Some(&depends));
1297 binary.set_section(Some("utils"));
1298 binary.set_priority(Some(Priority::Optional));
1299
1300 let output = binary.to_string();
1302 let lines: Vec<&str> = output.lines().collect();
1303
1304 let package_pos = lines
1306 .iter()
1307 .position(|l| l.starts_with("Package:"))
1308 .unwrap();
1309 let arch_pos = lines
1310 .iter()
1311 .position(|l| l.starts_with("Architecture:"))
1312 .unwrap();
1313 let section_pos = lines
1314 .iter()
1315 .position(|l| l.starts_with("Section:"))
1316 .unwrap();
1317 let priority_pos = lines
1318 .iter()
1319 .position(|l| l.starts_with("Priority:"))
1320 .unwrap();
1321 let depends_pos = lines
1322 .iter()
1323 .position(|l| l.starts_with("Depends:"))
1324 .unwrap();
1325 let desc_pos = lines
1326 .iter()
1327 .position(|l| l.starts_with("Description:"))
1328 .unwrap();
1329
1330 assert!(package_pos < arch_pos);
1332 assert!(arch_pos < section_pos);
1333 assert!(section_pos < priority_pos);
1334 assert!(priority_pos < depends_pos);
1335 assert!(depends_pos < desc_pos);
1336 }
1337
1338 #[test]
1339 fn test_parse() {
1340 let control: Control = r#"Source: foo
1341Section: libs
1342Priority: optional
1343Build-Depends: bar (>= 1.0.0), baz (>= 1.0.0)
1344Homepage: https://example.com
1345
1346"#
1347 .parse()
1348 .unwrap();
1349 let source = control.source().unwrap();
1350
1351 assert_eq!(source.name(), Some("foo".to_owned()));
1352 assert_eq!(source.section(), Some("libs".to_owned()));
1353 assert_eq!(source.priority(), Some(super::Priority::Optional));
1354 assert_eq!(
1355 source.homepage(),
1356 Some("https://example.com".parse().unwrap())
1357 );
1358 let bd = source.build_depends().unwrap();
1359 let entries = bd.entries().collect::<Vec<_>>();
1360 assert_eq!(entries.len(), 2);
1361 let rel = entries[0].relations().collect::<Vec<_>>().pop().unwrap();
1362 assert_eq!(rel.name(), "bar");
1363 assert_eq!(
1364 rel.version(),
1365 Some((
1366 VersionConstraint::GreaterThanEqual,
1367 "1.0.0".parse().unwrap()
1368 ))
1369 );
1370 let rel = entries[1].relations().collect::<Vec<_>>().pop().unwrap();
1371 assert_eq!(rel.name(), "baz");
1372 assert_eq!(
1373 rel.version(),
1374 Some((
1375 VersionConstraint::GreaterThanEqual,
1376 "1.0.0".parse().unwrap()
1377 ))
1378 );
1379 }
1380
1381 #[test]
1382 fn test_description() {
1383 let control: Control = r#"Source: foo
1384
1385Package: foo
1386Description: this is the short description
1387 And the longer one
1388 .
1389 is on the next lines
1390"#
1391 .parse()
1392 .unwrap();
1393 let binary = control.binaries().next().unwrap();
1394 assert_eq!(
1395 binary.description(),
1396 Some(
1397 "this is the short description\nAnd the longer one\n.\nis on the next lines"
1398 .to_owned()
1399 )
1400 );
1401 }
1402
1403 #[test]
1404 fn test_as_mut_deb822() {
1405 let mut control = Control::new();
1406 let deb822 = control.as_mut_deb822();
1407 let mut p = deb822.add_paragraph();
1408 p.set("Source", "foo");
1409 assert_eq!(control.source().unwrap().name(), Some("foo".to_owned()));
1410 }
1411
1412 #[test]
1413 fn test_as_deb822() {
1414 let control = Control::new();
1415 let _deb822: &Deb822 = control.as_deb822();
1416 }
1417
1418 #[test]
1419 fn test_set_depends() {
1420 let mut control = Control::new();
1421 let mut binary = control.add_binary("foo");
1422 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1423 binary.set_depends(Some(&relations));
1424 }
1425
1426 #[test]
1427 fn test_wrap_and_sort() {
1428 let mut control: Control = r#"Package: blah
1429Section: libs
1430
1431
1432
1433Package: foo
1434Description: this is a
1435 bar
1436 blah
1437"#
1438 .parse()
1439 .unwrap();
1440 control.wrap_and_sort(deb822_lossless::Indentation::Spaces(2), false, None);
1441 let expected = r#"Package: blah
1442Section: libs
1443
1444Package: foo
1445Description: this is a
1446 bar
1447 blah
1448"#
1449 .to_owned();
1450 assert_eq!(control.to_string(), expected);
1451 }
1452
1453 #[test]
1454 fn test_wrap_and_sort_source() {
1455 let mut control: Control = r#"Source: blah
1456Depends: foo, bar (<= 1.0.0)
1457
1458"#
1459 .parse()
1460 .unwrap();
1461 control.wrap_and_sort(deb822_lossless::Indentation::Spaces(2), true, None);
1462 let expected = r#"Source: blah
1463Depends: bar (<= 1.0.0), foo
1464"#
1465 .to_owned();
1466 assert_eq!(control.to_string(), expected);
1467 }
1468
1469 #[test]
1470 fn test_source_wrap_and_sort() {
1471 let control: Control = r#"Source: blah
1472Build-Depends: foo, bar (>= 1.0.0)
1473
1474"#
1475 .parse()
1476 .unwrap();
1477 let mut source = control.source().unwrap();
1478 source.wrap_and_sort(deb822_lossless::Indentation::Spaces(2), true, None);
1479 assert!(source.build_depends().is_some());
1483 }
1484
1485 #[test]
1486 fn test_binary_set_breaks() {
1487 let mut control = Control::new();
1488 let mut binary = control.add_binary("foo");
1489 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1490 binary.set_breaks(Some(&relations));
1491 assert!(binary.breaks().is_some());
1492 }
1493
1494 #[test]
1495 fn test_binary_set_pre_depends() {
1496 let mut control = Control::new();
1497 let mut binary = control.add_binary("foo");
1498 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1499 binary.set_pre_depends(Some(&relations));
1500 assert!(binary.pre_depends().is_some());
1501 }
1502
1503 #[test]
1504 fn test_binary_set_provides() {
1505 let mut control = Control::new();
1506 let mut binary = control.add_binary("foo");
1507 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1508 binary.set_provides(Some(&relations));
1509 assert!(binary.provides().is_some());
1510 }
1511
1512 #[test]
1513 fn test_source_build_conflicts() {
1514 let control: Control = r#"Source: blah
1515Build-Conflicts: foo, bar (>= 1.0.0)
1516
1517"#
1518 .parse()
1519 .unwrap();
1520 let source = control.source().unwrap();
1521 let conflicts = source.build_conflicts();
1522 assert!(conflicts.is_some());
1523 }
1524
1525 #[test]
1526 fn test_source_vcs_svn() {
1527 let control: Control = r#"Source: blah
1528Vcs-Svn: https://example.com/svn/repo
1529
1530"#
1531 .parse()
1532 .unwrap();
1533 let source = control.source().unwrap();
1534 assert_eq!(
1535 source.vcs_svn(),
1536 Some("https://example.com/svn/repo".to_string())
1537 );
1538 }
1539
1540 #[test]
1541 fn test_control_from_conversion() {
1542 let deb822_data = r#"Source: test
1543Section: libs
1544
1545"#;
1546 let deb822: Deb822 = deb822_data.parse().unwrap();
1547 let control = Control::from(deb822);
1548 assert!(control.source().is_some());
1549 }
1550
1551 #[test]
1552 fn test_fields_in_range() {
1553 let control_text = r#"Source: test-package
1554Maintainer: Test User <test@example.com>
1555Build-Depends: debhelper (>= 12)
1556
1557Package: test-binary
1558Architecture: any
1559Depends: ${shlibs:Depends}
1560Description: Test package
1561 This is a test package
1562"#;
1563 let control: Control = control_text.parse().unwrap();
1564
1565 let source_start = 0;
1567 let source_end = "Source: test-package".len();
1568 let source_range =
1569 rowan::TextRange::new((source_start as u32).into(), (source_end as u32).into());
1570
1571 let fields: Vec<_> = control.fields_in_range(source_range).collect();
1572 assert_eq!(fields.len(), 1);
1573 assert_eq!(fields[0].key(), Some("Source".to_string()));
1574
1575 let maintainer_start = control_text.find("Maintainer:").unwrap();
1577 let build_depends_end = control_text
1578 .find("Build-Depends: debhelper (>= 12)")
1579 .unwrap()
1580 + "Build-Depends: debhelper (>= 12)".len();
1581 let multi_range = rowan::TextRange::new(
1582 (maintainer_start as u32).into(),
1583 (build_depends_end as u32).into(),
1584 );
1585
1586 let fields: Vec<_> = control.fields_in_range(multi_range).collect();
1587 assert_eq!(fields.len(), 2);
1588 assert_eq!(fields[0].key(), Some("Maintainer".to_string()));
1589 assert_eq!(fields[1].key(), Some("Build-Depends".to_string()));
1590
1591 let cross_para_start = control_text.find("Build-Depends:").unwrap();
1593 let cross_para_end =
1594 control_text.find("Architecture: any").unwrap() + "Architecture: any".len();
1595 let cross_range = rowan::TextRange::new(
1596 (cross_para_start as u32).into(),
1597 (cross_para_end as u32).into(),
1598 );
1599
1600 let fields: Vec<_> = control.fields_in_range(cross_range).collect();
1601 assert_eq!(fields.len(), 3); assert_eq!(fields[0].key(), Some("Build-Depends".to_string()));
1603 assert_eq!(fields[1].key(), Some("Package".to_string()));
1604 assert_eq!(fields[2].key(), Some("Architecture".to_string()));
1605
1606 let empty_range = rowan::TextRange::new(1000.into(), 1001.into());
1608 let fields: Vec<_> = control.fields_in_range(empty_range).collect();
1609 assert_eq!(fields.len(), 0);
1610 }
1611
1612 #[test]
1613 fn test_source_overlaps_range() {
1614 let control_text = r#"Source: test-package
1615Maintainer: Test User <test@example.com>
1616
1617Package: test-binary
1618Architecture: any
1619"#;
1620 let control: Control = control_text.parse().unwrap();
1621 let source = control.source().unwrap();
1622
1623 let overlap_range = rowan::TextRange::new(10.into(), 30.into());
1625 assert!(source.overlaps_range(overlap_range));
1626
1627 let binary_start = control_text.find("Package:").unwrap();
1629 let no_overlap_range = rowan::TextRange::new(
1630 (binary_start as u32).into(),
1631 ((binary_start + 20) as u32).into(),
1632 );
1633 assert!(!source.overlaps_range(no_overlap_range));
1634
1635 let partial_overlap = rowan::TextRange::new(0.into(), 15.into());
1637 assert!(source.overlaps_range(partial_overlap));
1638 }
1639
1640 #[test]
1641 fn test_source_fields_in_range() {
1642 let control_text = r#"Source: test-package
1643Maintainer: Test User <test@example.com>
1644Build-Depends: debhelper (>= 12)
1645
1646Package: test-binary
1647"#;
1648 let control: Control = control_text.parse().unwrap();
1649 let source = control.source().unwrap();
1650
1651 let maintainer_start = control_text.find("Maintainer:").unwrap();
1653 let maintainer_end = maintainer_start + "Maintainer: Test User <test@example.com>".len();
1654 let maintainer_range = rowan::TextRange::new(
1655 (maintainer_start as u32).into(),
1656 (maintainer_end as u32).into(),
1657 );
1658
1659 let fields: Vec<_> = source.fields_in_range(maintainer_range).collect();
1660 assert_eq!(fields.len(), 1);
1661 assert_eq!(fields[0].key(), Some("Maintainer".to_string()));
1662
1663 let all_source_range = rowan::TextRange::new(0.into(), 100.into());
1665 let fields: Vec<_> = source.fields_in_range(all_source_range).collect();
1666 assert_eq!(fields.len(), 3); }
1668
1669 #[test]
1670 fn test_binary_overlaps_range() {
1671 let control_text = r#"Source: test-package
1672
1673Package: test-binary
1674Architecture: any
1675Depends: ${shlibs:Depends}
1676"#;
1677 let control: Control = control_text.parse().unwrap();
1678 let binary = control.binaries().next().unwrap();
1679
1680 let package_start = control_text.find("Package:").unwrap();
1682 let overlap_range = rowan::TextRange::new(
1683 (package_start as u32).into(),
1684 ((package_start + 30) as u32).into(),
1685 );
1686 assert!(binary.overlaps_range(overlap_range));
1687
1688 let no_overlap_range = rowan::TextRange::new(0.into(), 10.into());
1690 assert!(!binary.overlaps_range(no_overlap_range));
1691 }
1692
1693 #[test]
1694 fn test_binary_fields_in_range() {
1695 let control_text = r#"Source: test-package
1696
1697Package: test-binary
1698Architecture: any
1699Depends: ${shlibs:Depends}
1700Description: Test binary
1701 This is a test binary package
1702"#;
1703 let control: Control = control_text.parse().unwrap();
1704 let binary = control.binaries().next().unwrap();
1705
1706 let arch_start = control_text.find("Architecture:").unwrap();
1708 let depends_end = control_text.find("Depends: ${shlibs:Depends}").unwrap()
1709 + "Depends: ${shlibs:Depends}".len();
1710 let range = rowan::TextRange::new((arch_start as u32).into(), (depends_end as u32).into());
1711
1712 let fields: Vec<_> = binary.fields_in_range(range).collect();
1713 assert_eq!(fields.len(), 2);
1714 assert_eq!(fields[0].key(), Some("Architecture".to_string()));
1715 assert_eq!(fields[1].key(), Some("Depends".to_string()));
1716
1717 let desc_start = control_text.find("Description:").unwrap();
1719 let partial_range = rowan::TextRange::new(
1720 ((desc_start + 5) as u32).into(),
1721 ((desc_start + 15) as u32).into(),
1722 );
1723 let fields: Vec<_> = binary.fields_in_range(partial_range).collect();
1724 assert_eq!(fields.len(), 1);
1725 assert_eq!(fields[0].key(), Some("Description".to_string()));
1726 }
1727
1728 #[test]
1729 fn test_incremental_parsing_use_case() {
1730 let control_text = r#"Source: example
1732Maintainer: John Doe <john@example.com>
1733Standards-Version: 4.6.0
1734Build-Depends: debhelper-compat (= 13)
1735
1736Package: example-bin
1737Architecture: all
1738Depends: ${misc:Depends}
1739Description: Example package
1740 This is an example.
1741"#;
1742 let control: Control = control_text.parse().unwrap();
1743
1744 let change_start = control_text.find("Standards-Version:").unwrap();
1746 let change_end = change_start + "Standards-Version: 4.6.0".len();
1747 let change_range =
1748 rowan::TextRange::new((change_start as u32).into(), (change_end as u32).into());
1749
1750 let affected_fields: Vec<_> = control.fields_in_range(change_range).collect();
1752 assert_eq!(affected_fields.len(), 1);
1753 assert_eq!(
1754 affected_fields[0].key(),
1755 Some("Standards-Version".to_string())
1756 );
1757
1758 for entry in &affected_fields {
1760 let key = entry.key().unwrap();
1761 assert_ne!(key, "Maintainer");
1762 assert_ne!(key, "Build-Depends");
1763 assert_ne!(key, "Architecture");
1764 }
1765 }
1766
1767 #[test]
1768 fn test_positioned_parse_errors() {
1769 let input = "Invalid: field\nBroken field without colon";
1771 let parsed = Control::parse(input);
1772
1773 let positioned_errors = parsed.positioned_errors();
1775 assert!(
1776 !positioned_errors.is_empty(),
1777 "Should have positioned errors"
1778 );
1779
1780 for error in positioned_errors {
1782 let start_offset: u32 = error.range.start().into();
1783 let end_offset: u32 = error.range.end().into();
1784
1785 assert!(!error.message.is_empty());
1787
1788 assert!(start_offset <= end_offset);
1790 assert!(end_offset <= input.len() as u32);
1791
1792 assert!(error.code.is_some());
1794
1795 println!(
1796 "Error at {:?}: {} (code: {:?})",
1797 error.range, error.message, error.code
1798 );
1799 }
1800
1801 let string_errors = parsed.errors();
1803 assert!(!string_errors.is_empty());
1804 assert_eq!(string_errors.len(), positioned_errors.len());
1805 }
1806
1807 #[test]
1808 fn test_sort_binaries_basic() {
1809 let input = r#"Source: foo
1810
1811Package: libfoo
1812Architecture: all
1813
1814Package: libbar
1815Architecture: all
1816"#;
1817
1818 let mut control: Control = input.parse().unwrap();
1819 control.sort_binaries(false);
1820
1821 let binaries: Vec<_> = control.binaries().collect();
1822 assert_eq!(binaries.len(), 2);
1823 assert_eq!(binaries[0].name(), Some("libbar".to_string()));
1824 assert_eq!(binaries[1].name(), Some("libfoo".to_string()));
1825 }
1826
1827 #[test]
1828 fn test_sort_binaries_keep_first() {
1829 let input = r#"Source: foo
1830
1831Package: zzz-first
1832Architecture: all
1833
1834Package: libbar
1835Architecture: all
1836
1837Package: libaaa
1838Architecture: all
1839"#;
1840
1841 let mut control: Control = input.parse().unwrap();
1842 control.sort_binaries(true);
1843
1844 let binaries: Vec<_> = control.binaries().collect();
1845 assert_eq!(binaries.len(), 3);
1846 assert_eq!(binaries[0].name(), Some("zzz-first".to_string()));
1848 assert_eq!(binaries[1].name(), Some("libaaa".to_string()));
1850 assert_eq!(binaries[2].name(), Some("libbar".to_string()));
1851 }
1852
1853 #[test]
1854 fn test_sort_binaries_already_sorted() {
1855 let input = r#"Source: foo
1856
1857Package: aaa
1858Architecture: all
1859
1860Package: bbb
1861Architecture: all
1862
1863Package: ccc
1864Architecture: all
1865"#;
1866
1867 let mut control: Control = input.parse().unwrap();
1868 control.sort_binaries(false);
1869
1870 let binaries: Vec<_> = control.binaries().collect();
1871 assert_eq!(binaries.len(), 3);
1872 assert_eq!(binaries[0].name(), Some("aaa".to_string()));
1873 assert_eq!(binaries[1].name(), Some("bbb".to_string()));
1874 assert_eq!(binaries[2].name(), Some("ccc".to_string()));
1875 }
1876
1877 #[test]
1878 fn test_sort_binaries_no_binaries() {
1879 let input = r#"Source: foo
1880Maintainer: test@example.com
1881"#;
1882
1883 let mut control: Control = input.parse().unwrap();
1884 control.sort_binaries(false);
1885
1886 assert_eq!(control.binaries().count(), 0);
1888 }
1889
1890 #[test]
1891 fn test_sort_binaries_one_binary() {
1892 let input = r#"Source: foo
1893
1894Package: bar
1895Architecture: all
1896"#;
1897
1898 let mut control: Control = input.parse().unwrap();
1899 control.sort_binaries(false);
1900
1901 let binaries: Vec<_> = control.binaries().collect();
1902 assert_eq!(binaries.len(), 1);
1903 assert_eq!(binaries[0].name(), Some("bar".to_string()));
1904 }
1905
1906 #[test]
1907 fn test_sort_binaries_preserves_fields() {
1908 let input = r#"Source: foo
1909
1910Package: zzz
1911Architecture: any
1912Depends: libc6
1913Description: ZZZ package
1914
1915Package: aaa
1916Architecture: all
1917Depends: ${misc:Depends}
1918Description: AAA package
1919"#;
1920
1921 let mut control: Control = input.parse().unwrap();
1922 control.sort_binaries(false);
1923
1924 let binaries: Vec<_> = control.binaries().collect();
1925 assert_eq!(binaries.len(), 2);
1926
1927 assert_eq!(binaries[0].name(), Some("aaa".to_string()));
1929 assert_eq!(binaries[0].architecture(), Some("all".to_string()));
1930 assert_eq!(binaries[0].description(), Some("AAA package".to_string()));
1931
1932 assert_eq!(binaries[1].name(), Some("zzz".to_string()));
1934 assert_eq!(binaries[1].architecture(), Some("any".to_string()));
1935 assert_eq!(binaries[1].description(), Some("ZZZ package".to_string()));
1936 }
1937}