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 fields_in_range(
255 &self,
256 range: rowan::TextRange,
257 ) -> impl Iterator<Item = deb822_lossless::Entry> + '_ {
258 self.0
259 .paragraphs()
260 .flat_map(move |p| p.entries().collect::<Vec<_>>())
261 .filter(move |entry| {
262 let entry_range = entry.syntax().text_range();
263 entry_range.start() < range.end() && range.start() < entry_range.end()
265 })
266 }
267}
268
269impl From<Control> for Deb822 {
270 fn from(c: Control) -> Self {
271 c.0
272 }
273}
274
275impl From<Deb822> for Control {
276 fn from(d: Deb822) -> Self {
277 Control(d)
278 }
279}
280
281impl Default for Control {
282 fn default() -> Self {
283 Self::new()
284 }
285}
286
287impl std::str::FromStr for Control {
288 type Err = deb822_lossless::ParseError;
289
290 fn from_str(s: &str) -> Result<Self, Self::Err> {
291 Control::parse(s).to_result()
292 }
293}
294
295#[derive(Debug, Clone, PartialEq, Eq)]
297pub struct Source(Paragraph);
298
299impl From<Source> for Paragraph {
300 fn from(s: Source) -> Self {
301 s.0
302 }
303}
304
305impl From<Paragraph> for Source {
306 fn from(p: Paragraph) -> Self {
307 Source(p)
308 }
309}
310
311impl std::fmt::Display for Source {
312 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
313 self.0.fmt(f)
314 }
315}
316
317impl Source {
318 pub fn name(&self) -> Option<String> {
320 self.0.get("Source")
321 }
322
323 pub fn wrap_and_sort(
325 &mut self,
326 indentation: deb822_lossless::Indentation,
327 immediate_empty_line: bool,
328 max_line_length_one_liner: Option<usize>,
329 ) {
330 self.0 = self.0.wrap_and_sort(
331 indentation,
332 immediate_empty_line,
333 max_line_length_one_liner,
334 None,
335 Some(&format_field),
336 );
337 }
338
339 pub fn as_mut_deb822(&mut self) -> &mut Paragraph {
341 &mut self.0
342 }
343
344 pub fn as_deb822(&self) -> &Paragraph {
346 &self.0
347 }
348
349 pub fn set_name(&mut self, name: &str) {
351 self.set("Source", name);
352 }
353
354 pub fn section(&self) -> Option<String> {
356 self.0.get("Section")
357 }
358
359 pub fn set_section(&mut self, section: Option<&str>) {
361 if let Some(section) = section {
362 self.set("Section", section);
363 } else {
364 self.0.remove("Section");
365 }
366 }
367
368 pub fn priority(&self) -> Option<Priority> {
370 self.0.get("Priority").and_then(|v| v.parse().ok())
371 }
372
373 pub fn set_priority(&mut self, priority: Option<Priority>) {
375 if let Some(priority) = priority {
376 self.set("Priority", priority.to_string().as_str());
377 } else {
378 self.0.remove("Priority");
379 }
380 }
381
382 pub fn maintainer(&self) -> Option<String> {
384 self.0.get("Maintainer")
385 }
386
387 pub fn set_maintainer(&mut self, maintainer: &str) {
389 self.set("Maintainer", maintainer);
390 }
391
392 pub fn build_depends(&self) -> Option<Relations> {
394 self.0.get("Build-Depends").map(|s| s.parse().unwrap())
395 }
396
397 pub fn set_build_depends(&mut self, relations: &Relations) {
399 self.set("Build-Depends", relations.to_string().as_str());
400 }
401
402 pub fn build_depends_indep(&self) -> Option<Relations> {
404 self.0
405 .get("Build-Depends-Indep")
406 .map(|s| s.parse().unwrap())
407 }
408
409 pub fn build_depends_arch(&self) -> Option<Relations> {
411 self.0.get("Build-Depends-Arch").map(|s| s.parse().unwrap())
412 }
413
414 pub fn build_conflicts(&self) -> Option<Relations> {
416 self.0.get("Build-Conflicts").map(|s| s.parse().unwrap())
417 }
418
419 pub fn build_conflicts_indep(&self) -> Option<Relations> {
421 self.0
422 .get("Build-Conflicts-Indep")
423 .map(|s| s.parse().unwrap())
424 }
425
426 pub fn build_conflicts_arch(&self) -> Option<Relations> {
428 self.0
429 .get("Build-Conflicts-Arch")
430 .map(|s| s.parse().unwrap())
431 }
432
433 pub fn standards_version(&self) -> Option<String> {
435 self.0.get("Standards-Version")
436 }
437
438 pub fn set_standards_version(&mut self, version: &str) {
440 self.set("Standards-Version", version);
441 }
442
443 pub fn homepage(&self) -> Option<url::Url> {
445 self.0.get("Homepage").and_then(|s| s.parse().ok())
446 }
447
448 pub fn set_homepage(&mut self, homepage: &url::Url) {
450 self.set("Homepage", homepage.to_string().as_str());
451 }
452
453 pub fn vcs_git(&self) -> Option<String> {
455 self.0.get("Vcs-Git")
456 }
457
458 pub fn set_vcs_git(&mut self, url: &str) {
460 self.set("Vcs-Git", url);
461 }
462
463 pub fn vcs_svn(&self) -> Option<String> {
465 self.0.get("Vcs-Svn").map(|s| s.to_string())
466 }
467
468 pub fn set_vcs_svn(&mut self, url: &str) {
470 self.set("Vcs-Svn", url);
471 }
472
473 pub fn vcs_bzr(&self) -> Option<String> {
475 self.0.get("Vcs-Bzr").map(|s| s.to_string())
476 }
477
478 pub fn set_vcs_bzr(&mut self, url: &str) {
480 self.set("Vcs-Bzr", url);
481 }
482
483 pub fn vcs_arch(&self) -> Option<String> {
485 self.0.get("Vcs-Arch").map(|s| s.to_string())
486 }
487
488 pub fn set_vcs_arch(&mut self, url: &str) {
490 self.set("Vcs-Arch", url);
491 }
492
493 pub fn vcs_svk(&self) -> Option<String> {
495 self.0.get("Vcs-Svk").map(|s| s.to_string())
496 }
497
498 pub fn set_vcs_svk(&mut self, url: &str) {
500 self.set("Vcs-Svk", url);
501 }
502
503 pub fn vcs_darcs(&self) -> Option<String> {
505 self.0.get("Vcs-Darcs").map(|s| s.to_string())
506 }
507
508 pub fn set_vcs_darcs(&mut self, url: &str) {
510 self.set("Vcs-Darcs", url);
511 }
512
513 pub fn vcs_mtn(&self) -> Option<String> {
515 self.0.get("Vcs-Mtn").map(|s| s.to_string())
516 }
517
518 pub fn set_vcs_mtn(&mut self, url: &str) {
520 self.set("Vcs-Mtn", url);
521 }
522
523 pub fn vcs_cvs(&self) -> Option<String> {
525 self.0.get("Vcs-Cvs").map(|s| s.to_string())
526 }
527
528 pub fn set_vcs_cvs(&mut self, url: &str) {
530 self.set("Vcs-Cvs", url);
531 }
532
533 pub fn vcs_hg(&self) -> Option<String> {
535 self.0.get("Vcs-Hg").map(|s| s.to_string())
536 }
537
538 pub fn set_vcs_hg(&mut self, url: &str) {
540 self.set("Vcs-Hg", url);
541 }
542
543 pub fn set(&mut self, key: &str, value: &str) {
545 self.0.set_with_field_order(key, value, SOURCE_FIELD_ORDER);
546 }
547
548 pub fn vcs_browser(&self) -> Option<String> {
550 self.0.get("Vcs-Browser")
551 }
552
553 pub fn vcs(&self) -> Option<crate::vcs::Vcs> {
555 for (name, value) in self.0.items() {
556 if name.starts_with("Vcs-") && name != "Vcs-Browser" {
557 return crate::vcs::Vcs::from_field(&name, &value).ok();
558 }
559 }
560 None
561 }
562
563 pub fn set_vcs_browser(&mut self, url: Option<&str>) {
565 if let Some(url) = url {
566 self.set("Vcs-Browser", url);
567 } else {
568 self.0.remove("Vcs-Browser");
569 }
570 }
571
572 pub fn uploaders(&self) -> Option<Vec<String>> {
574 self.0
575 .get("Uploaders")
576 .map(|s| s.split(',').map(|s| s.trim().to_owned()).collect())
577 }
578
579 pub fn set_uploaders(&mut self, uploaders: &[&str]) {
581 self.set(
582 "Uploaders",
583 uploaders
584 .iter()
585 .map(|s| s.to_string())
586 .collect::<Vec<_>>()
587 .join(", ")
588 .as_str(),
589 );
590 }
591
592 pub fn architecture(&self) -> Option<String> {
594 self.0.get("Architecture")
595 }
596
597 pub fn set_architecture(&mut self, arch: Option<&str>) {
599 if let Some(arch) = arch {
600 self.set("Architecture", arch);
601 } else {
602 self.0.remove("Architecture");
603 }
604 }
605
606 pub fn rules_requires_root(&self) -> Option<bool> {
608 self.0
609 .get("Rules-Requires-Root")
610 .map(|s| match s.to_lowercase().as_str() {
611 "yes" => true,
612 "no" => false,
613 _ => panic!("invalid Rules-Requires-Root value"),
614 })
615 }
616
617 pub fn set_rules_requires_root(&mut self, requires_root: bool) {
619 self.set(
620 "Rules-Requires-Root",
621 if requires_root { "yes" } else { "no" },
622 );
623 }
624
625 pub fn testsuite(&self) -> Option<String> {
627 self.0.get("Testsuite")
628 }
629
630 pub fn set_testsuite(&mut self, testsuite: &str) {
632 self.set("Testsuite", testsuite);
633 }
634
635 pub fn overlaps_range(&self, range: rowan::TextRange) -> bool {
643 let para_range = self.0.syntax().text_range();
644 para_range.start() < range.end() && range.start() < para_range.end()
645 }
646
647 pub fn fields_in_range(
655 &self,
656 range: rowan::TextRange,
657 ) -> impl Iterator<Item = deb822_lossless::Entry> + '_ {
658 self.0.entries().filter(move |entry| {
659 let entry_range = entry.syntax().text_range();
660 entry_range.start() < range.end() && range.start() < entry_range.end()
661 })
662 }
663}
664
665#[cfg(feature = "python-debian")]
666impl<'py> pyo3::IntoPyObject<'py> for Source {
667 type Target = pyo3::PyAny;
668 type Output = pyo3::Bound<'py, Self::Target>;
669 type Error = pyo3::PyErr;
670
671 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
672 self.0.into_pyobject(py)
673 }
674}
675
676#[cfg(feature = "python-debian")]
677impl<'a, 'py> pyo3::IntoPyObject<'py> for &'a Source {
678 type Target = pyo3::PyAny;
679 type Output = pyo3::Bound<'py, Self::Target>;
680 type Error = pyo3::PyErr;
681
682 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
683 (&self.0).into_pyobject(py)
684 }
685}
686
687#[cfg(feature = "python-debian")]
688impl<'py> pyo3::FromPyObject<'_, 'py> for Source {
689 type Error = pyo3::PyErr;
690
691 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
692 Ok(Source(ob.extract()?))
693 }
694}
695
696impl std::fmt::Display for Control {
697 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
698 self.0.fmt(f)
699 }
700}
701
702impl AstNode for Control {
703 type Language = deb822_lossless::Lang;
704
705 fn can_cast(kind: <Self::Language as rowan::Language>::Kind) -> bool {
706 Deb822::can_cast(kind)
707 }
708
709 fn cast(syntax: rowan::SyntaxNode<Self::Language>) -> Option<Self> {
710 Deb822::cast(syntax).map(Control)
711 }
712
713 fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
714 self.0.syntax()
715 }
716}
717
718#[derive(Debug, Clone, PartialEq, Eq)]
720pub struct Binary(Paragraph);
721
722impl From<Binary> for Paragraph {
723 fn from(b: Binary) -> Self {
724 b.0
725 }
726}
727
728impl From<Paragraph> for Binary {
729 fn from(p: Paragraph) -> Self {
730 Binary(p)
731 }
732}
733
734#[cfg(feature = "python-debian")]
735impl<'py> pyo3::IntoPyObject<'py> for Binary {
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 Binary {
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 Binary {
758 type Error = pyo3::PyErr;
759
760 fn extract(ob: pyo3::Borrowed<'_, 'py, pyo3::PyAny>) -> Result<Self, Self::Error> {
761 Ok(Binary(ob.extract()?))
762 }
763}
764
765impl Default for Binary {
766 fn default() -> Self {
767 Self::new()
768 }
769}
770
771impl std::fmt::Display for Binary {
772 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
773 self.0.fmt(f)
774 }
775}
776
777impl Binary {
778 pub fn new() -> Self {
780 Binary(Paragraph::new())
781 }
782
783 pub fn as_mut_deb822(&mut self) -> &mut Paragraph {
785 &mut self.0
786 }
787
788 pub fn as_deb822(&self) -> &Paragraph {
790 &self.0
791 }
792
793 pub fn wrap_and_sort(
795 &mut self,
796 indentation: deb822_lossless::Indentation,
797 immediate_empty_line: bool,
798 max_line_length_one_liner: Option<usize>,
799 ) {
800 self.0 = self.0.wrap_and_sort(
801 indentation,
802 immediate_empty_line,
803 max_line_length_one_liner,
804 None,
805 Some(&format_field),
806 );
807 }
808
809 pub fn name(&self) -> Option<String> {
811 self.0.get("Package")
812 }
813
814 pub fn set_name(&mut self, name: &str) {
816 self.set("Package", name);
817 }
818
819 pub fn section(&self) -> Option<String> {
821 self.0.get("Section")
822 }
823
824 pub fn set_section(&mut self, section: Option<&str>) {
826 if let Some(section) = section {
827 self.set("Section", section);
828 } else {
829 self.0.remove("Section");
830 }
831 }
832
833 pub fn priority(&self) -> Option<Priority> {
835 self.0.get("Priority").and_then(|v| v.parse().ok())
836 }
837
838 pub fn set_priority(&mut self, priority: Option<Priority>) {
840 if let Some(priority) = priority {
841 self.set("Priority", priority.to_string().as_str());
842 } else {
843 self.0.remove("Priority");
844 }
845 }
846
847 pub fn architecture(&self) -> Option<String> {
849 self.0.get("Architecture")
850 }
851
852 pub fn set_architecture(&mut self, arch: Option<&str>) {
854 if let Some(arch) = arch {
855 self.set("Architecture", arch);
856 } else {
857 self.0.remove("Architecture");
858 }
859 }
860
861 pub fn depends(&self) -> Option<Relations> {
863 self.0.get("Depends").map(|s| s.parse().unwrap())
864 }
865
866 pub fn set_depends(&mut self, depends: Option<&Relations>) {
868 if let Some(depends) = depends {
869 self.set("Depends", depends.to_string().as_str());
870 } else {
871 self.0.remove("Depends");
872 }
873 }
874
875 pub fn recommends(&self) -> Option<Relations> {
877 self.0.get("Recommends").map(|s| s.parse().unwrap())
878 }
879
880 pub fn set_recommends(&mut self, recommends: Option<&Relations>) {
882 if let Some(recommends) = recommends {
883 self.set("Recommends", recommends.to_string().as_str());
884 } else {
885 self.0.remove("Recommends");
886 }
887 }
888
889 pub fn suggests(&self) -> Option<Relations> {
891 self.0.get("Suggests").map(|s| s.parse().unwrap())
892 }
893
894 pub fn set_suggests(&mut self, suggests: Option<&Relations>) {
896 if let Some(suggests) = suggests {
897 self.set("Suggests", suggests.to_string().as_str());
898 } else {
899 self.0.remove("Suggests");
900 }
901 }
902
903 pub fn enhances(&self) -> Option<Relations> {
905 self.0.get("Enhances").map(|s| s.parse().unwrap())
906 }
907
908 pub fn set_enhances(&mut self, enhances: Option<&Relations>) {
910 if let Some(enhances) = enhances {
911 self.set("Enhances", enhances.to_string().as_str());
912 } else {
913 self.0.remove("Enhances");
914 }
915 }
916
917 pub fn pre_depends(&self) -> Option<Relations> {
919 self.0.get("Pre-Depends").map(|s| s.parse().unwrap())
920 }
921
922 pub fn set_pre_depends(&mut self, pre_depends: Option<&Relations>) {
924 if let Some(pre_depends) = pre_depends {
925 self.set("Pre-Depends", pre_depends.to_string().as_str());
926 } else {
927 self.0.remove("Pre-Depends");
928 }
929 }
930
931 pub fn breaks(&self) -> Option<Relations> {
933 self.0.get("Breaks").map(|s| s.parse().unwrap())
934 }
935
936 pub fn set_breaks(&mut self, breaks: Option<&Relations>) {
938 if let Some(breaks) = breaks {
939 self.set("Breaks", breaks.to_string().as_str());
940 } else {
941 self.0.remove("Breaks");
942 }
943 }
944
945 pub fn conflicts(&self) -> Option<Relations> {
947 self.0.get("Conflicts").map(|s| s.parse().unwrap())
948 }
949
950 pub fn set_conflicts(&mut self, conflicts: Option<&Relations>) {
952 if let Some(conflicts) = conflicts {
953 self.set("Conflicts", conflicts.to_string().as_str());
954 } else {
955 self.0.remove("Conflicts");
956 }
957 }
958
959 pub fn replaces(&self) -> Option<Relations> {
961 self.0.get("Replaces").map(|s| s.parse().unwrap())
962 }
963
964 pub fn set_replaces(&mut self, replaces: Option<&Relations>) {
966 if let Some(replaces) = replaces {
967 self.set("Replaces", replaces.to_string().as_str());
968 } else {
969 self.0.remove("Replaces");
970 }
971 }
972
973 pub fn provides(&self) -> Option<Relations> {
975 self.0.get("Provides").map(|s| s.parse().unwrap())
976 }
977
978 pub fn set_provides(&mut self, provides: Option<&Relations>) {
980 if let Some(provides) = provides {
981 self.set("Provides", provides.to_string().as_str());
982 } else {
983 self.0.remove("Provides");
984 }
985 }
986
987 pub fn built_using(&self) -> Option<Relations> {
989 self.0.get("Built-Using").map(|s| s.parse().unwrap())
990 }
991
992 pub fn set_built_using(&mut self, built_using: Option<&Relations>) {
994 if let Some(built_using) = built_using {
995 self.set("Built-Using", built_using.to_string().as_str());
996 } else {
997 self.0.remove("Built-Using");
998 }
999 }
1000
1001 pub fn multi_arch(&self) -> Option<MultiArch> {
1003 self.0.get("Multi-Arch").map(|s| s.parse().unwrap())
1004 }
1005
1006 pub fn set_multi_arch(&mut self, multi_arch: Option<MultiArch>) {
1008 if let Some(multi_arch) = multi_arch {
1009 self.set("Multi-Arch", multi_arch.to_string().as_str());
1010 } else {
1011 self.0.remove("Multi-Arch");
1012 }
1013 }
1014
1015 pub fn essential(&self) -> bool {
1017 self.0.get("Essential").map(|s| s == "yes").unwrap_or(false)
1018 }
1019
1020 pub fn set_essential(&mut self, essential: bool) {
1022 if essential {
1023 self.set("Essential", "yes");
1024 } else {
1025 self.0.remove("Essential");
1026 }
1027 }
1028
1029 pub fn description(&self) -> Option<String> {
1031 self.0.get("Description")
1032 }
1033
1034 pub fn set_description(&mut self, description: Option<&str>) {
1036 if let Some(description) = description {
1037 self.set("Description", description);
1038 } else {
1039 self.0.remove("Description");
1040 }
1041 }
1042
1043 pub fn homepage(&self) -> Option<url::Url> {
1045 self.0.get("Homepage").and_then(|s| s.parse().ok())
1046 }
1047
1048 pub fn set_homepage(&mut self, url: &url::Url) {
1050 self.set("Homepage", url.as_str());
1051 }
1052
1053 pub fn set(&mut self, key: &str, value: &str) {
1055 self.0.set_with_field_order(key, value, BINARY_FIELD_ORDER);
1056 }
1057
1058 pub fn overlaps_range(&self, range: rowan::TextRange) -> bool {
1066 let para_range = self.0.syntax().text_range();
1067 para_range.start() < range.end() && range.start() < para_range.end()
1068 }
1069
1070 pub fn fields_in_range(
1078 &self,
1079 range: rowan::TextRange,
1080 ) -> impl Iterator<Item = deb822_lossless::Entry> + '_ {
1081 self.0.entries().filter(move |entry| {
1082 let entry_range = entry.syntax().text_range();
1083 entry_range.start() < range.end() && range.start() < entry_range.end()
1084 })
1085 }
1086}
1087
1088#[cfg(test)]
1089mod tests {
1090 use super::*;
1091 use crate::relations::VersionConstraint;
1092
1093 #[test]
1094 fn test_source_set_field_ordering() {
1095 let mut control = Control::new();
1096 let mut source = control.add_source("mypackage");
1097
1098 source.set("Homepage", "https://example.com");
1100 source.set("Build-Depends", "debhelper");
1101 source.set("Standards-Version", "4.5.0");
1102 source.set("Maintainer", "Test <test@example.com>");
1103
1104 let output = source.to_string();
1106 let lines: Vec<&str> = output.lines().collect();
1107
1108 assert!(lines[0].starts_with("Source:"));
1110
1111 let maintainer_pos = lines
1113 .iter()
1114 .position(|l| l.starts_with("Maintainer:"))
1115 .unwrap();
1116 let build_depends_pos = lines
1117 .iter()
1118 .position(|l| l.starts_with("Build-Depends:"))
1119 .unwrap();
1120 let standards_pos = lines
1121 .iter()
1122 .position(|l| l.starts_with("Standards-Version:"))
1123 .unwrap();
1124 let homepage_pos = lines
1125 .iter()
1126 .position(|l| l.starts_with("Homepage:"))
1127 .unwrap();
1128
1129 assert!(maintainer_pos < build_depends_pos);
1131 assert!(build_depends_pos < standards_pos);
1132 assert!(standards_pos < homepage_pos);
1133 }
1134
1135 #[test]
1136 fn test_binary_set_field_ordering() {
1137 let mut control = Control::new();
1138 let mut binary = control.add_binary("mypackage");
1139
1140 binary.set("Description", "A test package");
1142 binary.set("Architecture", "amd64");
1143 binary.set("Depends", "libc6");
1144 binary.set("Section", "utils");
1145
1146 let output = binary.to_string();
1148 let lines: Vec<&str> = output.lines().collect();
1149
1150 assert!(lines[0].starts_with("Package:"));
1152
1153 let arch_pos = lines
1155 .iter()
1156 .position(|l| l.starts_with("Architecture:"))
1157 .unwrap();
1158 let section_pos = lines
1159 .iter()
1160 .position(|l| l.starts_with("Section:"))
1161 .unwrap();
1162 let depends_pos = lines
1163 .iter()
1164 .position(|l| l.starts_with("Depends:"))
1165 .unwrap();
1166 let desc_pos = lines
1167 .iter()
1168 .position(|l| l.starts_with("Description:"))
1169 .unwrap();
1170
1171 assert!(arch_pos < section_pos);
1173 assert!(section_pos < depends_pos);
1174 assert!(depends_pos < desc_pos);
1175 }
1176
1177 #[test]
1178 fn test_source_specific_set_methods_use_field_ordering() {
1179 let mut control = Control::new();
1180 let mut source = control.add_source("mypackage");
1181
1182 source.set_homepage(&"https://example.com".parse().unwrap());
1184 source.set_maintainer("Test <test@example.com>");
1185 source.set_standards_version("4.5.0");
1186 source.set_vcs_git("https://github.com/example/repo");
1187
1188 let output = source.to_string();
1190 let lines: Vec<&str> = output.lines().collect();
1191
1192 let source_pos = lines.iter().position(|l| l.starts_with("Source:")).unwrap();
1194 let maintainer_pos = lines
1195 .iter()
1196 .position(|l| l.starts_with("Maintainer:"))
1197 .unwrap();
1198 let standards_pos = lines
1199 .iter()
1200 .position(|l| l.starts_with("Standards-Version:"))
1201 .unwrap();
1202 let vcs_git_pos = lines
1203 .iter()
1204 .position(|l| l.starts_with("Vcs-Git:"))
1205 .unwrap();
1206 let homepage_pos = lines
1207 .iter()
1208 .position(|l| l.starts_with("Homepage:"))
1209 .unwrap();
1210
1211 assert!(source_pos < maintainer_pos);
1213 assert!(maintainer_pos < standards_pos);
1214 assert!(standards_pos < vcs_git_pos);
1215 assert!(vcs_git_pos < homepage_pos);
1216 }
1217
1218 #[test]
1219 fn test_binary_specific_set_methods_use_field_ordering() {
1220 let mut control = Control::new();
1221 let mut binary = control.add_binary("mypackage");
1222
1223 binary.set_description(Some("A test package"));
1225 binary.set_architecture(Some("amd64"));
1226 let depends = "libc6".parse().unwrap();
1227 binary.set_depends(Some(&depends));
1228 binary.set_section(Some("utils"));
1229 binary.set_priority(Some(Priority::Optional));
1230
1231 let output = binary.to_string();
1233 let lines: Vec<&str> = output.lines().collect();
1234
1235 let package_pos = lines
1237 .iter()
1238 .position(|l| l.starts_with("Package:"))
1239 .unwrap();
1240 let arch_pos = lines
1241 .iter()
1242 .position(|l| l.starts_with("Architecture:"))
1243 .unwrap();
1244 let section_pos = lines
1245 .iter()
1246 .position(|l| l.starts_with("Section:"))
1247 .unwrap();
1248 let priority_pos = lines
1249 .iter()
1250 .position(|l| l.starts_with("Priority:"))
1251 .unwrap();
1252 let depends_pos = lines
1253 .iter()
1254 .position(|l| l.starts_with("Depends:"))
1255 .unwrap();
1256 let desc_pos = lines
1257 .iter()
1258 .position(|l| l.starts_with("Description:"))
1259 .unwrap();
1260
1261 assert!(package_pos < arch_pos);
1263 assert!(arch_pos < section_pos);
1264 assert!(section_pos < priority_pos);
1265 assert!(priority_pos < depends_pos);
1266 assert!(depends_pos < desc_pos);
1267 }
1268
1269 #[test]
1270 fn test_parse() {
1271 let control: Control = r#"Source: foo
1272Section: libs
1273Priority: optional
1274Build-Depends: bar (>= 1.0.0), baz (>= 1.0.0)
1275Homepage: https://example.com
1276
1277"#
1278 .parse()
1279 .unwrap();
1280 let source = control.source().unwrap();
1281
1282 assert_eq!(source.name(), Some("foo".to_owned()));
1283 assert_eq!(source.section(), Some("libs".to_owned()));
1284 assert_eq!(source.priority(), Some(super::Priority::Optional));
1285 assert_eq!(
1286 source.homepage(),
1287 Some("https://example.com".parse().unwrap())
1288 );
1289 let bd = source.build_depends().unwrap();
1290 let entries = bd.entries().collect::<Vec<_>>();
1291 assert_eq!(entries.len(), 2);
1292 let rel = entries[0].relations().collect::<Vec<_>>().pop().unwrap();
1293 assert_eq!(rel.name(), "bar");
1294 assert_eq!(
1295 rel.version(),
1296 Some((
1297 VersionConstraint::GreaterThanEqual,
1298 "1.0.0".parse().unwrap()
1299 ))
1300 );
1301 let rel = entries[1].relations().collect::<Vec<_>>().pop().unwrap();
1302 assert_eq!(rel.name(), "baz");
1303 assert_eq!(
1304 rel.version(),
1305 Some((
1306 VersionConstraint::GreaterThanEqual,
1307 "1.0.0".parse().unwrap()
1308 ))
1309 );
1310 }
1311
1312 #[test]
1313 fn test_description() {
1314 let control: Control = r#"Source: foo
1315
1316Package: foo
1317Description: this is the short description
1318 And the longer one
1319 .
1320 is on the next lines
1321"#
1322 .parse()
1323 .unwrap();
1324 let binary = control.binaries().next().unwrap();
1325 assert_eq!(
1326 binary.description(),
1327 Some(
1328 "this is the short description\nAnd the longer one\n.\nis on the next lines"
1329 .to_owned()
1330 )
1331 );
1332 }
1333
1334 #[test]
1335 fn test_as_mut_deb822() {
1336 let mut control = Control::new();
1337 let deb822 = control.as_mut_deb822();
1338 let mut p = deb822.add_paragraph();
1339 p.set("Source", "foo");
1340 assert_eq!(control.source().unwrap().name(), Some("foo".to_owned()));
1341 }
1342
1343 #[test]
1344 fn test_as_deb822() {
1345 let control = Control::new();
1346 let _deb822: &Deb822 = control.as_deb822();
1347 }
1348
1349 #[test]
1350 fn test_set_depends() {
1351 let mut control = Control::new();
1352 let mut binary = control.add_binary("foo");
1353 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1354 binary.set_depends(Some(&relations));
1355 }
1356
1357 #[test]
1358 fn test_wrap_and_sort() {
1359 let mut control: Control = r#"Package: blah
1360Section: libs
1361
1362
1363
1364Package: foo
1365Description: this is a
1366 bar
1367 blah
1368"#
1369 .parse()
1370 .unwrap();
1371 control.wrap_and_sort(deb822_lossless::Indentation::Spaces(2), false, None);
1372 let expected = r#"Package: blah
1373Section: libs
1374
1375Package: foo
1376Description: this is a
1377 bar
1378 blah
1379"#
1380 .to_owned();
1381 assert_eq!(control.to_string(), expected);
1382 }
1383
1384 #[test]
1385 fn test_wrap_and_sort_source() {
1386 let mut control: Control = r#"Source: blah
1387Depends: foo, bar (<= 1.0.0)
1388
1389"#
1390 .parse()
1391 .unwrap();
1392 control.wrap_and_sort(deb822_lossless::Indentation::Spaces(2), true, None);
1393 let expected = r#"Source: blah
1394Depends: bar (<= 1.0.0), foo
1395"#
1396 .to_owned();
1397 assert_eq!(control.to_string(), expected);
1398 }
1399
1400 #[test]
1401 fn test_source_wrap_and_sort() {
1402 let control: Control = r#"Source: blah
1403Build-Depends: foo, bar (>= 1.0.0)
1404
1405"#
1406 .parse()
1407 .unwrap();
1408 let mut source = control.source().unwrap();
1409 source.wrap_and_sort(deb822_lossless::Indentation::Spaces(2), true, None);
1410 assert!(source.build_depends().is_some());
1414 }
1415
1416 #[test]
1417 fn test_binary_set_breaks() {
1418 let mut control = Control::new();
1419 let mut binary = control.add_binary("foo");
1420 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1421 binary.set_breaks(Some(&relations));
1422 assert!(binary.breaks().is_some());
1423 }
1424
1425 #[test]
1426 fn test_binary_set_pre_depends() {
1427 let mut control = Control::new();
1428 let mut binary = control.add_binary("foo");
1429 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1430 binary.set_pre_depends(Some(&relations));
1431 assert!(binary.pre_depends().is_some());
1432 }
1433
1434 #[test]
1435 fn test_binary_set_provides() {
1436 let mut control = Control::new();
1437 let mut binary = control.add_binary("foo");
1438 let relations: Relations = "bar (>= 1.0.0)".parse().unwrap();
1439 binary.set_provides(Some(&relations));
1440 assert!(binary.provides().is_some());
1441 }
1442
1443 #[test]
1444 fn test_source_build_conflicts() {
1445 let control: Control = r#"Source: blah
1446Build-Conflicts: foo, bar (>= 1.0.0)
1447
1448"#
1449 .parse()
1450 .unwrap();
1451 let source = control.source().unwrap();
1452 let conflicts = source.build_conflicts();
1453 assert!(conflicts.is_some());
1454 }
1455
1456 #[test]
1457 fn test_source_vcs_svn() {
1458 let control: Control = r#"Source: blah
1459Vcs-Svn: https://example.com/svn/repo
1460
1461"#
1462 .parse()
1463 .unwrap();
1464 let source = control.source().unwrap();
1465 assert_eq!(
1466 source.vcs_svn(),
1467 Some("https://example.com/svn/repo".to_string())
1468 );
1469 }
1470
1471 #[test]
1472 fn test_control_from_conversion() {
1473 let deb822_data = r#"Source: test
1474Section: libs
1475
1476"#;
1477 let deb822: Deb822 = deb822_data.parse().unwrap();
1478 let control = Control::from(deb822);
1479 assert!(control.source().is_some());
1480 }
1481
1482 #[test]
1483 fn test_fields_in_range() {
1484 let control_text = r#"Source: test-package
1485Maintainer: Test User <test@example.com>
1486Build-Depends: debhelper (>= 12)
1487
1488Package: test-binary
1489Architecture: any
1490Depends: ${shlibs:Depends}
1491Description: Test package
1492 This is a test package
1493"#;
1494 let control: Control = control_text.parse().unwrap();
1495
1496 let source_start = 0;
1498 let source_end = "Source: test-package".len();
1499 let source_range =
1500 rowan::TextRange::new((source_start as u32).into(), (source_end as u32).into());
1501
1502 let fields: Vec<_> = control.fields_in_range(source_range).collect();
1503 assert_eq!(fields.len(), 1);
1504 assert_eq!(fields[0].key(), Some("Source".to_string()));
1505
1506 let maintainer_start = control_text.find("Maintainer:").unwrap();
1508 let build_depends_end = control_text
1509 .find("Build-Depends: debhelper (>= 12)")
1510 .unwrap()
1511 + "Build-Depends: debhelper (>= 12)".len();
1512 let multi_range = rowan::TextRange::new(
1513 (maintainer_start as u32).into(),
1514 (build_depends_end as u32).into(),
1515 );
1516
1517 let fields: Vec<_> = control.fields_in_range(multi_range).collect();
1518 assert_eq!(fields.len(), 2);
1519 assert_eq!(fields[0].key(), Some("Maintainer".to_string()));
1520 assert_eq!(fields[1].key(), Some("Build-Depends".to_string()));
1521
1522 let cross_para_start = control_text.find("Build-Depends:").unwrap();
1524 let cross_para_end =
1525 control_text.find("Architecture: any").unwrap() + "Architecture: any".len();
1526 let cross_range = rowan::TextRange::new(
1527 (cross_para_start as u32).into(),
1528 (cross_para_end as u32).into(),
1529 );
1530
1531 let fields: Vec<_> = control.fields_in_range(cross_range).collect();
1532 assert_eq!(fields.len(), 3); assert_eq!(fields[0].key(), Some("Build-Depends".to_string()));
1534 assert_eq!(fields[1].key(), Some("Package".to_string()));
1535 assert_eq!(fields[2].key(), Some("Architecture".to_string()));
1536
1537 let empty_range = rowan::TextRange::new(1000.into(), 1001.into());
1539 let fields: Vec<_> = control.fields_in_range(empty_range).collect();
1540 assert_eq!(fields.len(), 0);
1541 }
1542
1543 #[test]
1544 fn test_source_overlaps_range() {
1545 let control_text = r#"Source: test-package
1546Maintainer: Test User <test@example.com>
1547
1548Package: test-binary
1549Architecture: any
1550"#;
1551 let control: Control = control_text.parse().unwrap();
1552 let source = control.source().unwrap();
1553
1554 let overlap_range = rowan::TextRange::new(10.into(), 30.into());
1556 assert!(source.overlaps_range(overlap_range));
1557
1558 let binary_start = control_text.find("Package:").unwrap();
1560 let no_overlap_range = rowan::TextRange::new(
1561 (binary_start as u32).into(),
1562 ((binary_start + 20) as u32).into(),
1563 );
1564 assert!(!source.overlaps_range(no_overlap_range));
1565
1566 let partial_overlap = rowan::TextRange::new(0.into(), 15.into());
1568 assert!(source.overlaps_range(partial_overlap));
1569 }
1570
1571 #[test]
1572 fn test_source_fields_in_range() {
1573 let control_text = r#"Source: test-package
1574Maintainer: Test User <test@example.com>
1575Build-Depends: debhelper (>= 12)
1576
1577Package: test-binary
1578"#;
1579 let control: Control = control_text.parse().unwrap();
1580 let source = control.source().unwrap();
1581
1582 let maintainer_start = control_text.find("Maintainer:").unwrap();
1584 let maintainer_end = maintainer_start + "Maintainer: Test User <test@example.com>".len();
1585 let maintainer_range = rowan::TextRange::new(
1586 (maintainer_start as u32).into(),
1587 (maintainer_end as u32).into(),
1588 );
1589
1590 let fields: Vec<_> = source.fields_in_range(maintainer_range).collect();
1591 assert_eq!(fields.len(), 1);
1592 assert_eq!(fields[0].key(), Some("Maintainer".to_string()));
1593
1594 let all_source_range = rowan::TextRange::new(0.into(), 100.into());
1596 let fields: Vec<_> = source.fields_in_range(all_source_range).collect();
1597 assert_eq!(fields.len(), 3); }
1599
1600 #[test]
1601 fn test_binary_overlaps_range() {
1602 let control_text = r#"Source: test-package
1603
1604Package: test-binary
1605Architecture: any
1606Depends: ${shlibs:Depends}
1607"#;
1608 let control: Control = control_text.parse().unwrap();
1609 let binary = control.binaries().next().unwrap();
1610
1611 let package_start = control_text.find("Package:").unwrap();
1613 let overlap_range = rowan::TextRange::new(
1614 (package_start as u32).into(),
1615 ((package_start + 30) as u32).into(),
1616 );
1617 assert!(binary.overlaps_range(overlap_range));
1618
1619 let no_overlap_range = rowan::TextRange::new(0.into(), 10.into());
1621 assert!(!binary.overlaps_range(no_overlap_range));
1622 }
1623
1624 #[test]
1625 fn test_binary_fields_in_range() {
1626 let control_text = r#"Source: test-package
1627
1628Package: test-binary
1629Architecture: any
1630Depends: ${shlibs:Depends}
1631Description: Test binary
1632 This is a test binary package
1633"#;
1634 let control: Control = control_text.parse().unwrap();
1635 let binary = control.binaries().next().unwrap();
1636
1637 let arch_start = control_text.find("Architecture:").unwrap();
1639 let depends_end = control_text.find("Depends: ${shlibs:Depends}").unwrap()
1640 + "Depends: ${shlibs:Depends}".len();
1641 let range = rowan::TextRange::new((arch_start as u32).into(), (depends_end as u32).into());
1642
1643 let fields: Vec<_> = binary.fields_in_range(range).collect();
1644 assert_eq!(fields.len(), 2);
1645 assert_eq!(fields[0].key(), Some("Architecture".to_string()));
1646 assert_eq!(fields[1].key(), Some("Depends".to_string()));
1647
1648 let desc_start = control_text.find("Description:").unwrap();
1650 let partial_range = rowan::TextRange::new(
1651 ((desc_start + 5) as u32).into(),
1652 ((desc_start + 15) as u32).into(),
1653 );
1654 let fields: Vec<_> = binary.fields_in_range(partial_range).collect();
1655 assert_eq!(fields.len(), 1);
1656 assert_eq!(fields[0].key(), Some("Description".to_string()));
1657 }
1658
1659 #[test]
1660 fn test_incremental_parsing_use_case() {
1661 let control_text = r#"Source: example
1663Maintainer: John Doe <john@example.com>
1664Standards-Version: 4.6.0
1665Build-Depends: debhelper-compat (= 13)
1666
1667Package: example-bin
1668Architecture: all
1669Depends: ${misc:Depends}
1670Description: Example package
1671 This is an example.
1672"#;
1673 let control: Control = control_text.parse().unwrap();
1674
1675 let change_start = control_text.find("Standards-Version:").unwrap();
1677 let change_end = change_start + "Standards-Version: 4.6.0".len();
1678 let change_range =
1679 rowan::TextRange::new((change_start as u32).into(), (change_end as u32).into());
1680
1681 let affected_fields: Vec<_> = control.fields_in_range(change_range).collect();
1683 assert_eq!(affected_fields.len(), 1);
1684 assert_eq!(
1685 affected_fields[0].key(),
1686 Some("Standards-Version".to_string())
1687 );
1688
1689 for entry in &affected_fields {
1691 let key = entry.key().unwrap();
1692 assert_ne!(key, "Maintainer");
1693 assert_ne!(key, "Build-Depends");
1694 assert_ne!(key, "Architecture");
1695 }
1696 }
1697
1698 #[test]
1699 fn test_positioned_parse_errors() {
1700 let input = "Invalid: field\nBroken field without colon";
1702 let parsed = Control::parse(input);
1703
1704 let positioned_errors = parsed.positioned_errors();
1706 assert!(
1707 !positioned_errors.is_empty(),
1708 "Should have positioned errors"
1709 );
1710
1711 for error in positioned_errors {
1713 let start_offset: u32 = error.range.start().into();
1714 let end_offset: u32 = error.range.end().into();
1715
1716 assert!(!error.message.is_empty());
1718
1719 assert!(start_offset <= end_offset);
1721 assert!(end_offset <= input.len() as u32);
1722
1723 assert!(error.code.is_some());
1725
1726 println!(
1727 "Error at {:?}: {} (code: {:?})",
1728 error.range, error.message, error.code
1729 );
1730 }
1731
1732 let string_errors = parsed.errors();
1734 assert!(!string_errors.is_empty());
1735 assert_eq!(string_errors.len(), positioned_errors.len());
1736 }
1737}