1#[cfg(feature = "derive")]
7pub use deb822_derive::{FromDeb822, ToDeb822};
8
9pub mod convert;
10pub use convert::{FromDeb822Paragraph, ToDeb822Paragraph};
11
12pub const SOURCE_FIELD_ORDER: &[&str] = &[
14 "Source",
15 "Section",
16 "Priority",
17 "Maintainer",
18 "Uploaders",
19 "Build-Depends",
20 "Build-Depends-Indep",
21 "Build-Depends-Arch",
22 "Build-Conflicts",
23 "Build-Conflicts-Indep",
24 "Build-Conflicts-Arch",
25 "Standards-Version",
26 "Vcs-Browser",
27 "Vcs-Git",
28 "Vcs-Svn",
29 "Vcs-Bzr",
30 "Vcs-Hg",
31 "Vcs-Darcs",
32 "Vcs-Cvs",
33 "Vcs-Arch",
34 "Vcs-Mtn",
35 "Homepage",
36 "Rules-Requires-Root",
37 "Testsuite",
38 "Testsuite-Triggers",
39];
40
41pub const BINARY_FIELD_ORDER: &[&str] = &[
43 "Package",
44 "Architecture",
45 "Section",
46 "Priority",
47 "Multi-Arch",
48 "Essential",
49 "Build-Profiles",
50 "Built-Using",
51 "Pre-Depends",
52 "Depends",
53 "Recommends",
54 "Suggests",
55 "Enhances",
56 "Conflicts",
57 "Breaks",
58 "Replaces",
59 "Provides",
60 "Description",
61];
62
63#[derive(Debug)]
65pub enum Error {
66 UnexpectedToken(String),
68
69 UnexpectedEof,
71
72 ExpectedEof,
74
75 Io(std::io::Error),
77}
78
79impl From<std::io::Error> for Error {
80 fn from(e: std::io::Error) -> Self {
81 Self::Io(e)
82 }
83}
84
85impl std::fmt::Display for Error {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
87 match self {
88 Self::UnexpectedToken(t) => write!(f, "Unexpected token: {}", t),
89 Self::UnexpectedEof => f.write_str("Unexpected end-of-file"),
90 Self::Io(e) => write!(f, "IO error: {}", e),
91 Self::ExpectedEof => f.write_str("Expected end-of-file"),
92 }
93 }
94}
95
96impl std::error::Error for Error {
97 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
98 match self {
99 Self::Io(e) => Some(e),
100 _ => None,
101 }
102 }
103}
104
105#[derive(Debug, PartialEq, Eq, Clone)]
107pub struct Field {
108 pub name: String,
110
111 pub value: String,
113}
114
115#[derive(Debug, PartialEq, Eq, Clone)]
117pub struct Paragraph {
118 pub fields: Vec<Field>,
120}
121
122impl Paragraph {
123 pub fn get(&self, name: &str) -> Option<&str> {
127 for field in &self.fields {
128 if field.name == name {
129 return Some(&field.value);
130 }
131 }
132 None
133 }
134
135 pub fn is_empty(&self) -> bool {
137 self.fields.is_empty()
138 }
139
140 pub fn len(&self) -> usize {
142 self.fields.len()
143 }
144
145 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
147 self.fields
148 .iter()
149 .map(|field| (field.name.as_str(), field.value.as_str()))
150 }
151
152 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut String)> {
154 self.fields
155 .iter_mut()
156 .map(|field| (field.name.as_str(), &mut field.value))
157 }
158
159 pub fn insert(&mut self, name: &str, value: &str) {
164 self.fields.push(Field {
165 name: name.to_string(),
166 value: value.to_string(),
167 });
168 }
169
170 pub fn set(&mut self, name: &str, value: &str) {
176 for field in &mut self.fields {
178 if field.name == name {
179 field.value = value.to_string();
180 return;
181 }
182 }
183
184 let field_order = if self.fields.iter().any(|f| f.name == "Source") {
187 SOURCE_FIELD_ORDER
188 } else if self.fields.iter().any(|f| f.name == "Package") {
189 BINARY_FIELD_ORDER
190 } else {
191 if name == "Source" {
193 SOURCE_FIELD_ORDER
194 } else if name == "Package" {
195 BINARY_FIELD_ORDER
196 } else {
197 let has_source_fields = self.fields.iter().any(|f| {
199 SOURCE_FIELD_ORDER.contains(&f.name.as_str())
200 && !BINARY_FIELD_ORDER.contains(&f.name.as_str())
201 });
202 if has_source_fields {
203 SOURCE_FIELD_ORDER
204 } else {
205 BINARY_FIELD_ORDER
206 }
207 }
208 };
209
210 let insertion_index = self.find_insertion_index(name, field_order);
211 self.fields.insert(
212 insertion_index,
213 Field {
214 name: name.to_string(),
215 value: value.to_string(),
216 },
217 );
218 }
219
220 pub fn set_with_field_order(&mut self, name: &str, value: &str, field_order: &[&str]) {
222 for field in &mut self.fields {
224 if field.name == name {
225 field.value = value.to_string();
226 return;
227 }
228 }
229
230 let insertion_index = self.find_insertion_index(name, field_order);
231 self.fields.insert(
232 insertion_index,
233 Field {
234 name: name.to_string(),
235 value: value.to_string(),
236 },
237 );
238 }
239
240 fn find_insertion_index(&self, name: &str, field_order: &[&str]) -> usize {
242 let new_field_position = field_order.iter().position(|&field| field == name);
244
245 let mut insertion_index = self.fields.len();
246
247 for (i, field) in self.fields.iter().enumerate() {
249 let existing_position = field_order.iter().position(|&f| f == field.name);
250
251 match (new_field_position, existing_position) {
252 (Some(new_pos), Some(existing_pos)) => {
254 if new_pos < existing_pos {
255 insertion_index = i;
256 break;
257 }
258 }
259 (Some(_), None) => {
261 }
263 (None, Some(_)) => {
265 }
267 (None, None) => {
269 if name < &field.name {
270 insertion_index = i;
271 break;
272 }
273 }
274 }
275 }
276
277 if new_field_position.is_some() && insertion_index == self.fields.len() {
280 for (i, field) in self.fields.iter().enumerate().rev() {
282 if field_order.iter().any(|&f| f == field.name) {
283 insertion_index = i + 1;
285 break;
286 }
287 }
288 }
289
290 insertion_index
291 }
292
293 pub fn remove(&mut self, name: &str) {
295 self.fields.retain(|field| field.name != name);
296 }
297}
298
299impl std::fmt::Display for Field {
300 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
301 let lines = self.value.lines().collect::<Vec<_>>();
302 if lines.len() > 1 {
303 write!(f, "{}:", self.name)?;
304 for line in lines {
305 writeln!(f, " {}", line)?;
306 }
307 Ok(())
308 } else {
309 writeln!(f, "{}: {}", self.name, self.value)
310 }
311 }
312}
313
314impl std::fmt::Display for Paragraph {
315 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
316 for field in &self.fields {
317 field.fmt(f)?;
318 }
319 Ok(())
320 }
321}
322
323impl std::fmt::Display for Deb822 {
324 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
325 for (i, paragraph) in self.0.iter().enumerate() {
326 if i > 0 {
327 writeln!(f)?;
328 }
329 write!(f, "{}", paragraph)?;
330 }
331 Ok(())
332 }
333}
334
335impl std::str::FromStr for Paragraph {
336 type Err = Error;
337
338 fn from_str(s: &str) -> Result<Self, Self::Err> {
339 let doc: Deb822 = s.parse()?;
340 if doc.is_empty() {
341 Err(Error::UnexpectedEof)
342 } else if doc.len() > 1 {
343 Err(Error::ExpectedEof)
344 } else {
345 Ok(doc.0.into_iter().next().unwrap())
346 }
347 }
348}
349
350impl From<Vec<(String, String)>> for Paragraph {
351 fn from(fields: Vec<(String, String)>) -> Self {
352 fields.into_iter().collect()
353 }
354}
355
356impl FromIterator<(String, String)> for Paragraph {
357 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
358 let fields = iter
359 .into_iter()
360 .map(|(name, value)| Field { name, value })
361 .collect();
362 Paragraph { fields }
363 }
364}
365
366impl IntoIterator for Paragraph {
367 type Item = (String, String);
368 type IntoIter = std::iter::Map<std::vec::IntoIter<Field>, fn(Field) -> (String, String)>;
369
370 fn into_iter(self) -> Self::IntoIter {
371 self.fields
372 .into_iter()
373 .map(|field| (field.name, field.value))
374 }
375}
376
377#[derive(Debug, PartialEq, Eq, Clone)]
379pub struct Deb822(Vec<Paragraph>);
380
381impl From<Deb822> for Vec<Paragraph> {
382 fn from(doc: Deb822) -> Self {
383 doc.0
384 }
385}
386
387impl IntoIterator for Deb822 {
388 type Item = Paragraph;
389 type IntoIter = std::vec::IntoIter<Paragraph>;
390
391 fn into_iter(self) -> Self::IntoIter {
392 self.0.into_iter()
393 }
394}
395
396impl Deb822 {
397 pub fn len(&self) -> usize {
399 self.0.len()
400 }
401
402 pub fn is_empty(&self) -> bool {
404 self.0.is_empty()
405 }
406
407 pub fn iter(&self) -> impl Iterator<Item = &Paragraph> {
409 self.0.iter()
410 }
411
412 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Paragraph> {
414 self.0.iter_mut()
415 }
416
417 pub fn from_reader<R: std::io::Read>(mut r: R) -> Result<Self, Error> {
419 let mut buf = String::new();
420 r.read_to_string(&mut buf)?;
421 buf.parse()
422 }
423
424 pub fn iter_paragraphs_from_reader<R: std::io::BufRead>(reader: R) -> ParagraphReader<R> {
429 ParagraphReader::new(reader)
430 }
431}
432
433pub struct ParagraphReader<R: std::io::BufRead> {
435 reader: R,
436 buffer: String,
437 finished: bool,
438}
439
440impl<R: std::io::BufRead> ParagraphReader<R> {
441 pub fn new(reader: R) -> Self {
443 Self {
444 reader,
445 buffer: String::new(),
446 finished: false,
447 }
448 }
449}
450
451impl<R: std::io::BufRead> Iterator for ParagraphReader<R> {
452 type Item = Result<Paragraph, Error>;
453
454 fn next(&mut self) -> Option<Self::Item> {
455 if self.finished {
456 return None;
457 }
458
459 self.buffer.clear();
460 let mut found_content = false;
461
462 loop {
463 let mut line = String::new();
464 match self.reader.read_line(&mut line) {
465 Ok(0) => {
466 self.finished = true;
468 if found_content {
469 return Some(self.buffer.parse());
471 }
472 return None;
473 }
474 Ok(_) => {
475 if line.trim().is_empty() && found_content {
477 return Some(self.buffer.parse());
479 }
480
481 if !found_content
483 && (line.trim().is_empty() || line.trim_start().starts_with('#'))
484 {
485 continue;
486 }
487
488 if !line.starts_with(|c: char| c.is_whitespace()) && line.contains(':') {
490 found_content = true;
491 } else if found_content {
492 } else if !line.trim_start().starts_with('#') {
494 found_content = true;
496 }
497
498 self.buffer.push_str(&line);
499 }
500 Err(e) => {
501 self.finished = true;
502 return Some(Err(Error::Io(e)));
503 }
504 }
505 }
506 }
507}
508
509impl std::str::FromStr for Deb822 {
510 type Err = Error;
511
512 fn from_str(s: &str) -> Result<Self, Self::Err> {
513 let bytes = s.as_bytes();
515 let mut paragraphs = Vec::new();
516 let mut pos = 0;
517 let len = bytes.len();
518
519 while pos < len {
520 while pos < len {
522 let b = bytes[pos];
523 if b == b'#' {
524 while pos < len && bytes[pos] != b'\n' {
525 pos += 1;
526 }
527 if pos < len {
528 pos += 1;
529 }
530 } else if b == b'\n' || b == b'\r' {
531 pos += 1;
532 } else {
533 break;
534 }
535 }
536
537 if pos >= len {
538 break;
539 }
540
541 if bytes[pos] == b' ' || bytes[pos] == b'\t' {
543 let line_start = pos;
544 while pos < len && bytes[pos] != b'\n' {
545 pos += 1;
546 }
547 let token = unsafe { std::str::from_utf8_unchecked(&bytes[line_start..pos]) };
548 return Err(Error::UnexpectedToken(token.to_string()));
549 }
550
551 let mut fields: Vec<Field> = Vec::new();
553
554 loop {
555 if pos >= len {
556 break;
557 }
558
559 if bytes[pos] == b'\n' {
561 pos += 1;
562 break;
563 }
564
565 if bytes[pos] == b'#' {
567 while pos < len && bytes[pos] != b'\n' {
568 pos += 1;
569 }
570 if pos < len {
571 pos += 1;
572 }
573 continue;
574 }
575
576 if bytes[pos] == b' ' || bytes[pos] == b'\t' {
578 if fields.is_empty() {
579 let line_start = pos;
581 while pos < len && bytes[pos] != b'\n' {
582 pos += 1;
583 }
584 let token =
585 unsafe { std::str::from_utf8_unchecked(&bytes[line_start..pos]) };
586 return Err(Error::UnexpectedToken(token.to_string()));
587 }
588
589 while pos < len && (bytes[pos] == b' ' || bytes[pos] == b'\t') {
591 pos += 1;
592 }
593
594 let line_start = pos;
596 while pos < len && bytes[pos] != b'\n' {
597 pos += 1;
598 }
599
600 if let Some(last_field) = fields.last_mut() {
602 last_field.value.push('\n');
603 last_field.value.push_str(unsafe {
604 std::str::from_utf8_unchecked(&bytes[line_start..pos])
605 });
606 }
607
608 if pos < len {
609 pos += 1; }
611 continue;
612 }
613
614 let name_start = pos;
616 while pos < len && bytes[pos] != b':' && bytes[pos] != b'\n' {
617 pos += 1;
618 }
619
620 if pos >= len || bytes[pos] != b':' {
621 let line_start = name_start;
623 while pos < len && bytes[pos] != b'\n' {
624 pos += 1;
625 }
626 let token = unsafe { std::str::from_utf8_unchecked(&bytes[line_start..pos]) };
627 return Err(Error::UnexpectedToken(token.to_string()));
628 }
629
630 let name = unsafe { std::str::from_utf8_unchecked(&bytes[name_start..pos]) };
631
632 if name.is_empty() {
634 let line_start = name_start;
635 let mut end = pos;
636 while end < len && bytes[end] != b'\n' {
637 end += 1;
638 }
639 let token = unsafe { std::str::from_utf8_unchecked(&bytes[line_start..end]) };
640 return Err(Error::UnexpectedToken(token.to_string()));
641 }
642
643 pos += 1; while pos < len && (bytes[pos] == b' ' || bytes[pos] == b'\t') {
647 pos += 1;
648 }
649
650 let value_start = pos;
652 while pos < len && bytes[pos] != b'\n' {
653 pos += 1;
654 }
655
656 let value = unsafe { std::str::from_utf8_unchecked(&bytes[value_start..pos]) };
657
658 fields.push(Field {
659 name: name.to_string(),
660 value: value.to_string(),
661 });
662
663 if pos < len {
664 pos += 1; }
666 }
667
668 if !fields.is_empty() {
669 paragraphs.push(Paragraph { fields });
670 }
671 }
672
673 Ok(Deb822(paragraphs))
674 }
675}
676
677#[cfg(test)]
678mod tests {
679 use super::*;
680
681 #[test]
682 fn test_error_display() {
683 let err = Error::UnexpectedToken("invalid".to_string());
684 assert_eq!(err.to_string(), "Unexpected token: invalid");
685
686 let err = Error::UnexpectedEof;
687 assert_eq!(err.to_string(), "Unexpected end-of-file");
688
689 let err = Error::ExpectedEof;
690 assert_eq!(err.to_string(), "Expected end-of-file");
691
692 let io_err = std::io::Error::other("test error");
693 let err = Error::Io(io_err);
694 assert!(err.to_string().contains("IO error: test error"));
695 }
696
697 #[test]
698 fn test_parse() {
699 let input = r#"Package: hello
700Version: 2.10
701Description: A program that says hello
702 Some more text
703
704Package: world
705Version: 1.0
706Description: A program that says world
707 And some more text
708Another-Field: value
709
710# A comment
711
712"#;
713
714 let mut deb822: Deb822 = input.parse().unwrap();
715 assert_eq!(
716 deb822,
717 Deb822(vec![
718 Paragraph {
719 fields: vec![
720 Field {
721 name: "Package".to_string(),
722 value: "hello".to_string(),
723 },
724 Field {
725 name: "Version".to_string(),
726 value: "2.10".to_string(),
727 },
728 Field {
729 name: "Description".to_string(),
730 value: "A program that says hello\nSome more text".to_string(),
731 },
732 ],
733 },
734 Paragraph {
735 fields: vec![
736 Field {
737 name: "Package".to_string(),
738 value: "world".to_string(),
739 },
740 Field {
741 name: "Version".to_string(),
742 value: "1.0".to_string(),
743 },
744 Field {
745 name: "Description".to_string(),
746 value: "A program that says world\nAnd some more text".to_string(),
747 },
748 Field {
749 name: "Another-Field".to_string(),
750 value: "value".to_string(),
751 },
752 ],
753 },
754 ])
755 );
756 assert_eq!(deb822.len(), 2);
757 assert!(!deb822.is_empty());
758 assert_eq!(deb822.iter().count(), 2);
759
760 let para = deb822.iter().next().unwrap();
761 assert_eq!(para.get("Package"), Some("hello"));
762 assert_eq!(para.get("Version"), Some("2.10"));
763 assert_eq!(
764 para.get("Description"),
765 Some("A program that says hello\nSome more text")
766 );
767 assert_eq!(para.get("Another-Field"), None);
768 assert!(!para.is_empty());
769 assert_eq!(para.len(), 3);
770 assert_eq!(
771 para.iter().collect::<Vec<_>>(),
772 vec![
773 ("Package", "hello"),
774 ("Version", "2.10"),
775 ("Description", "A program that says hello\nSome more text"),
776 ]
777 );
778 let para = deb822.iter_mut().next().unwrap();
779 para.insert("Another-Field", "value");
780 assert_eq!(para.get("Another-Field"), Some("value"));
781
782 let mut newpara = Paragraph { fields: vec![] };
783 newpara.insert("Package", "new");
784 assert_eq!(newpara.to_string(), "Package: new\n");
785 }
786
787 #[test]
788 fn test_paragraph_iter() {
789 let input = r#"Package: hello
790Version: 2.10
791"#;
792 let para: Paragraph = input.parse().unwrap();
793 let mut iter = para.into_iter();
794 assert_eq!(
795 iter.next(),
796 Some(("Package".to_string(), "hello".to_string()))
797 );
798 assert_eq!(
799 iter.next(),
800 Some(("Version".to_string(), "2.10".to_string()))
801 );
802 assert_eq!(iter.next(), None);
803 }
804
805 #[test]
806 fn test_format_multiline() {
807 let para = Paragraph {
808 fields: vec![Field {
809 name: "Description".to_string(),
810 value: "A program that says hello\nSome more text".to_string(),
811 }],
812 };
813
814 assert_eq!(
815 para.to_string(),
816 "Description: A program that says hello\n Some more text\n"
817 );
818 }
819
820 #[test]
821 fn test_paragraph_from_str_errors() {
822 let result = "Package: foo\n\nPackage: bar\n".parse::<Paragraph>();
824 assert!(matches!(result, Err(Error::ExpectedEof)));
825
826 let result = "".parse::<Paragraph>();
828 assert!(matches!(result, Err(Error::UnexpectedEof)));
829 }
830
831 #[test]
832 fn test_from_vec() {
833 let fields = vec![
834 ("Package".to_string(), "hello".to_string()),
835 ("Version".to_string(), "1.0".to_string()),
836 ];
837
838 let para: Paragraph = fields.into();
839 assert_eq!(para.get("Package"), Some("hello"));
840 assert_eq!(para.get("Version"), Some("1.0"));
841 }
842
843 #[test]
844 fn test_unexpected_tokens() {
845 let input = "Value before key\nPackage: hello\n";
847 let result = input.parse::<Deb822>();
848 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
849
850 let input = "Package hello\n";
852 let result = input.parse::<Deb822>();
853 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
854
855 let input = " Indented: value\n";
857 let result = input.parse::<Deb822>();
858 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
859
860 let input = "Key: value\nvalue without key\n";
862 let result = input.parse::<Deb822>();
863 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
864
865 let input = "Key: value\n:\n";
867 let result = input.parse::<Deb822>();
868 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
869 }
870
871 #[test]
872 fn test_parse_continuation_with_colon() {
873 let input = "Package: test\nDescription: short\n line: with colon\n";
875 let result = input.parse::<Deb822>();
876 assert!(result.is_ok());
877
878 let deb822 = result.unwrap();
879 assert_eq!(deb822.0.len(), 1);
880 assert_eq!(deb822.0[0].fields.len(), 2);
881 assert_eq!(deb822.0[0].fields[0].name, "Package");
882 assert_eq!(deb822.0[0].fields[0].value, "test");
883 assert_eq!(deb822.0[0].fields[1].name, "Description");
884 assert_eq!(deb822.0[0].fields[1].value, "short\nline: with colon");
885 }
886
887 #[test]
888 fn test_parse_continuation_starting_with_colon() {
889 let input = "Package: test\nDescription: short\n :value\n";
891 let result = input.parse::<Deb822>();
892 assert!(result.is_ok());
893
894 let deb822 = result.unwrap();
895 assert_eq!(deb822.0.len(), 1);
896 assert_eq!(deb822.0[0].fields.len(), 2);
897 assert_eq!(deb822.0[0].fields[0].name, "Package");
898 assert_eq!(deb822.0[0].fields[0].value, "test");
899 assert_eq!(deb822.0[0].fields[1].name, "Description");
900 assert_eq!(deb822.0[0].fields[1].value, "short\n:value");
901 }
902
903 #[test]
904 fn test_from_reader() {
905 let input = "Package: hello\nVersion: 1.0\n";
907 let result = Deb822::from_reader(input.as_bytes()).unwrap();
908 assert_eq!(result.len(), 1);
909 let para = result.iter().next().unwrap();
910 assert_eq!(para.get("Package"), Some("hello"));
911
912 use std::io::Error as IoError;
914 struct FailingReader;
915 impl std::io::Read for FailingReader {
916 fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
917 Err(IoError::other("test error"))
918 }
919 }
920
921 let result = Deb822::from_reader(FailingReader);
922 assert!(matches!(result, Err(Error::Io(_))));
923 }
924
925 #[test]
926 fn test_deb822_vec_conversion() {
927 let paragraphs = vec![
928 Paragraph {
929 fields: vec![Field {
930 name: "Package".to_string(),
931 value: "hello".to_string(),
932 }],
933 },
934 Paragraph {
935 fields: vec![Field {
936 name: "Package".to_string(),
937 value: "world".to_string(),
938 }],
939 },
940 ];
941
942 let deb822 = Deb822(paragraphs.clone());
943 let vec: Vec<Paragraph> = deb822.into();
944 assert_eq!(vec, paragraphs);
945 }
946
947 #[test]
948 fn test_deb822_iteration() {
949 let paragraphs = vec![
950 Paragraph {
951 fields: vec![Field {
952 name: "Package".to_string(),
953 value: "hello".to_string(),
954 }],
955 },
956 Paragraph {
957 fields: vec![Field {
958 name: "Package".to_string(),
959 value: "world".to_string(),
960 }],
961 },
962 ];
963
964 let deb822 = Deb822(paragraphs.clone());
965
966 let collected: Vec<_> = deb822.into_iter().collect();
968 assert_eq!(collected, paragraphs);
969
970 let deb822 = Deb822(paragraphs.clone());
972 let iter_refs: Vec<&Paragraph> = deb822.iter().collect();
973 assert_eq!(iter_refs.len(), 2);
974 assert_eq!(iter_refs[0].get("Package"), Some("hello"));
975
976 let mut deb822 = Deb822(paragraphs.clone());
977 for para in deb822.iter_mut() {
978 if para.get("Package") == Some("hello") {
979 para.set("Version", "1.0");
980 }
981 }
982 assert_eq!(deb822.iter().next().unwrap().get("Version"), Some("1.0"));
983 }
984
985 #[test]
986 fn test_empty_collections() {
987 let deb822 = Deb822(vec![]);
989 assert!(deb822.is_empty());
990 assert_eq!(deb822.len(), 0);
991 assert_eq!(deb822.iter().count(), 0);
992
993 let para = Paragraph { fields: vec![] };
995 assert!(para.is_empty());
996 assert_eq!(para.len(), 0);
997 assert_eq!(para.iter().count(), 0);
998 assert_eq!(para.get("Any"), None);
999
1000 assert_eq!(para.to_string(), "");
1002
1003 assert_eq!(deb822.to_string(), "");
1005 }
1006
1007 #[test]
1008 fn test_paragraph_mutable_iteration() {
1009 let mut para = Paragraph {
1010 fields: vec![
1011 Field {
1012 name: "First".to_string(),
1013 value: "1".to_string(),
1014 },
1015 Field {
1016 name: "Second".to_string(),
1017 value: "2".to_string(),
1018 },
1019 ],
1020 };
1021
1022 for (_, value) in para.iter_mut() {
1024 *value = format!("{}0", value);
1025 }
1026
1027 assert_eq!(para.get("First"), Some("10"));
1028 assert_eq!(para.get("Second"), Some("20"));
1029 }
1030
1031 #[test]
1032 fn test_insert_duplicate_key() {
1033 let mut para = Paragraph {
1034 fields: vec![Field {
1035 name: "Key".to_string(),
1036 value: "Value1".to_string(),
1037 }],
1038 };
1039
1040 para.insert("Key", "Value2");
1042
1043 assert_eq!(para.fields.len(), 2);
1044 assert_eq!(para.fields[0].value, "Value1");
1045 assert_eq!(para.fields[1].value, "Value2");
1046
1047 assert_eq!(para.get("Key"), Some("Value1"));
1049 }
1050
1051 #[test]
1052 fn test_multiline_field_format() {
1053 let field = Field {
1055 name: "MultiField".to_string(),
1056 value: "line1\nline2\nline3".to_string(),
1057 };
1058
1059 let formatted = format!("{}", field);
1060 assert_eq!(formatted, "MultiField: line1\n line2\n line3\n");
1061
1062 let para = Paragraph {
1064 fields: vec![field],
1065 };
1066
1067 let formatted = format!("{}", para);
1068 assert_eq!(formatted, "MultiField: line1\n line2\n line3\n");
1069 }
1070
1071 #[test]
1072 fn test_paragraph_parsing_edge_cases() {
1073 let input = "Key:\n";
1075 let para: Paragraph = input.parse().unwrap();
1076 assert_eq!(para.get("Key"), Some(""));
1077
1078 let input = "Key: \n";
1081 let para: Paragraph = input.parse().unwrap();
1082 assert_eq!(para.get("Key"), Some(""));
1083
1084 let input = "Key1: value1\n\n\n\nKey2: value2\n";
1086 let deb822: Deb822 = input.parse().unwrap();
1087 assert_eq!(deb822.len(), 2);
1088
1089 let input = "Key: value\n with\n indentation\n levels\n";
1092 let para: Paragraph = input.parse().unwrap();
1093 assert_eq!(para.get("Key"), Some("value\nwith\nindentation\nlevels"));
1094 }
1095
1096 #[test]
1097 fn test_parse_complex() {
1098 let input = "# Comment at start\nKey1: val1\nKey2: \n indented\nKey3: val3\n\n# Comment between paragraphs\n\nKey4: val4\n";
1100 let deb822: Deb822 = input.parse().unwrap();
1101
1102 assert_eq!(deb822.len(), 2);
1103 let paragraphs: Vec<Paragraph> = deb822.into();
1104
1105 assert_eq!(paragraphs[0].get("Key2"), Some("\nindented"));
1106 assert_eq!(paragraphs[1].get("Key4"), Some("val4"));
1107
1108 let input = "Key:\n indented value\n";
1110 let para: Paragraph = input.parse().unwrap();
1111 assert_eq!(para.get("Key"), Some("\nindented value"));
1112 }
1113
1114 #[test]
1115 fn test_deb822_display() {
1116 let para1 = Paragraph {
1118 fields: vec![Field {
1119 name: "Key1".to_string(),
1120 value: "Value1".to_string(),
1121 }],
1122 };
1123
1124 let para2 = Paragraph {
1125 fields: vec![Field {
1126 name: "Key2".to_string(),
1127 value: "Value2".to_string(),
1128 }],
1129 };
1130
1131 let deb822 = Deb822(vec![para1, para2]);
1132 let formatted = format!("{}", deb822);
1133
1134 assert_eq!(formatted, "Key1: Value1\n\nKey2: Value2\n");
1135 }
1136
1137 #[test]
1138 fn test_parser_edge_cases() {
1139 let input = "# Comment\nKey: value";
1143 let deb822: Deb822 = input.parse().unwrap();
1144 assert_eq!(deb822.len(), 1);
1145
1146 let input = "Key: value\n .indented";
1148 let deb822: Deb822 = input.parse().unwrap();
1149 assert_eq!(
1150 deb822.iter().next().unwrap().get("Key"),
1151 Some("value\n.indented")
1152 );
1153
1154 let input = "Key: value\n line1\n line2\n\nNextKey: value";
1156 let deb822: Deb822 = input.parse().unwrap();
1157 assert_eq!(deb822.len(), 2);
1158 assert_eq!(
1159 deb822.iter().next().unwrap().get("Key"),
1160 Some("value\nline1\nline2")
1161 );
1162 }
1163
1164 #[test]
1165 fn test_iter_paragraphs_from_reader() {
1166 use std::io::BufReader;
1167
1168 let input = r#"Package: hello
1169Version: 2.10
1170Description: A program that says hello
1171 Some more text
1172
1173Package: world
1174Version: 1.0
1175Description: A program that says world
1176 And some more text
1177Another-Field: value
1178
1179# A comment
1180
1181"#;
1182
1183 let reader = BufReader::new(input.as_bytes());
1184 let paragraphs: Result<Vec<_>, _> = Deb822::iter_paragraphs_from_reader(reader).collect();
1185 let paragraphs = paragraphs.unwrap();
1186
1187 assert_eq!(paragraphs.len(), 2);
1188
1189 assert_eq!(paragraphs[0].get("Package"), Some("hello"));
1190 assert_eq!(paragraphs[0].get("Version"), Some("2.10"));
1191 assert_eq!(
1192 paragraphs[0].get("Description"),
1193 Some("A program that says hello\nSome more text")
1194 );
1195
1196 assert_eq!(paragraphs[1].get("Package"), Some("world"));
1197 assert_eq!(paragraphs[1].get("Version"), Some("1.0"));
1198 assert_eq!(
1199 paragraphs[1].get("Description"),
1200 Some("A program that says world\nAnd some more text")
1201 );
1202 assert_eq!(paragraphs[1].get("Another-Field"), Some("value"));
1203 }
1204
1205 #[test]
1206 fn test_iter_paragraphs_from_reader_empty() {
1207 use std::io::BufReader;
1208
1209 let input = "";
1210 let reader = BufReader::new(input.as_bytes());
1211 let paragraphs: Result<Vec<_>, _> = Deb822::iter_paragraphs_from_reader(reader).collect();
1212 let paragraphs = paragraphs.unwrap();
1213
1214 assert_eq!(paragraphs.len(), 0);
1215 }
1216
1217 #[test]
1218 fn test_iter_paragraphs_from_reader_with_leading_comments() {
1219 use std::io::BufReader;
1220
1221 let input = r#"# Leading comment
1222# Another comment
1223
1224Package: test
1225Version: 1.0
1226"#;
1227
1228 let reader = BufReader::new(input.as_bytes());
1229 let paragraphs: Result<Vec<_>, _> = Deb822::iter_paragraphs_from_reader(reader).collect();
1230 let paragraphs = paragraphs.unwrap();
1231
1232 assert_eq!(paragraphs.len(), 1);
1233 assert_eq!(paragraphs[0].get("Package"), Some("test"));
1234 }
1235}