1#[cfg(feature = "derive")]
39pub use deb822_derive::{FromDeb822, ToDeb822};
40
41pub mod convert;
42pub use convert::{FromDeb822Paragraph, ToDeb822Paragraph};
43
44pub mod borrowed;
45
46pub const SOURCE_FIELD_ORDER: &[&str] = &[
48 "Source",
49 "Section",
50 "Priority",
51 "Maintainer",
52 "Uploaders",
53 "Build-Depends",
54 "Build-Depends-Indep",
55 "Build-Depends-Arch",
56 "Build-Conflicts",
57 "Build-Conflicts-Indep",
58 "Build-Conflicts-Arch",
59 "Standards-Version",
60 "Vcs-Browser",
61 "Vcs-Git",
62 "Vcs-Svn",
63 "Vcs-Bzr",
64 "Vcs-Hg",
65 "Vcs-Darcs",
66 "Vcs-Cvs",
67 "Vcs-Arch",
68 "Vcs-Mtn",
69 "Homepage",
70 "Rules-Requires-Root",
71 "Testsuite",
72 "Testsuite-Triggers",
73];
74
75pub const BINARY_FIELD_ORDER: &[&str] = &[
77 "Package",
78 "Architecture",
79 "Section",
80 "Priority",
81 "Multi-Arch",
82 "Essential",
83 "Build-Profiles",
84 "Built-Using",
85 "Pre-Depends",
86 "Depends",
87 "Recommends",
88 "Suggests",
89 "Enhances",
90 "Conflicts",
91 "Breaks",
92 "Replaces",
93 "Provides",
94 "Description",
95];
96
97#[derive(Debug)]
99pub enum Error {
100 UnexpectedToken(String),
102
103 UnexpectedEof,
105
106 ExpectedEof,
108
109 Io(std::io::Error),
111}
112
113impl From<std::io::Error> for Error {
114 fn from(e: std::io::Error) -> Self {
115 Self::Io(e)
116 }
117}
118
119impl std::fmt::Display for Error {
120 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
121 match self {
122 Self::UnexpectedToken(t) => write!(f, "Unexpected token: {}", t),
123 Self::UnexpectedEof => f.write_str("Unexpected end-of-file"),
124 Self::Io(e) => write!(f, "IO error: {}", e),
125 Self::ExpectedEof => f.write_str("Expected end-of-file"),
126 }
127 }
128}
129
130impl std::error::Error for Error {
131 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
132 match self {
133 Self::Io(e) => Some(e),
134 _ => None,
135 }
136 }
137}
138
139#[derive(Debug, PartialEq, Eq, Clone)]
141pub struct Field {
142 pub name: String,
144
145 pub value: String,
147}
148
149#[derive(Debug, PartialEq, Eq, Clone)]
151pub struct Paragraph {
152 pub fields: Vec<Field>,
154}
155
156impl Paragraph {
157 pub fn get(&self, name: &str) -> Option<&str> {
162 for field in &self.fields {
163 if field.name.eq_ignore_ascii_case(name) {
164 return Some(&field.value);
165 }
166 }
167 None
168 }
169
170 pub fn is_empty(&self) -> bool {
172 self.fields.is_empty()
173 }
174
175 pub fn len(&self) -> usize {
177 self.fields.len()
178 }
179
180 pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
182 self.fields
183 .iter()
184 .map(|field| (field.name.as_str(), field.value.as_str()))
185 }
186
187 pub fn iter_mut(&mut self) -> impl Iterator<Item = (&str, &mut String)> {
189 self.fields
190 .iter_mut()
191 .map(|field| (field.name.as_str(), &mut field.value))
192 }
193
194 pub fn insert(&mut self, name: &str, value: &str) {
199 self.fields.push(Field {
200 name: name.to_string(),
201 value: value.to_string(),
202 });
203 }
204
205 pub fn set(&mut self, name: &str, value: &str) {
212 for field in &mut self.fields {
214 if field.name.eq_ignore_ascii_case(name) {
215 field.value = value.to_string();
216 return;
217 }
218 }
219
220 let insertion_index = self.fields.len();
222 self.fields.insert(
223 insertion_index,
224 Field {
225 name: name.to_string(),
226 value: value.to_string(),
227 },
228 );
229 }
230
231 pub fn set_with_field_order(&mut self, name: &str, value: &str, field_order: &[&str]) {
235 for field in &mut self.fields {
237 if field.name.eq_ignore_ascii_case(name) {
238 field.value = value.to_string();
239 return;
240 }
241 }
242
243 let insertion_index = self.find_insertion_index(name, field_order);
244 self.fields.insert(
245 insertion_index,
246 Field {
247 name: name.to_string(),
248 value: value.to_string(),
249 },
250 );
251 }
252
253 fn find_insertion_index(&self, name: &str, field_order: &[&str]) -> usize {
255 let new_field_position = field_order
257 .iter()
258 .position(|&field| field.eq_ignore_ascii_case(name));
259
260 let mut insertion_index = self.fields.len();
261
262 for (i, field) in self.fields.iter().enumerate() {
264 let existing_position = field_order
265 .iter()
266 .position(|&f| f.eq_ignore_ascii_case(&field.name));
267
268 match (new_field_position, existing_position) {
269 (Some(new_pos), Some(existing_pos)) => {
271 if new_pos < existing_pos {
272 insertion_index = i;
273 break;
274 }
275 }
276 (Some(_), None) => {
278 }
280 (None, Some(_)) => {
282 }
284 (None, None) => {
286 if name < &field.name {
287 insertion_index = i;
288 break;
289 }
290 }
291 }
292 }
293
294 if new_field_position.is_some() && insertion_index == self.fields.len() {
297 for (i, field) in self.fields.iter().enumerate().rev() {
299 if field_order
300 .iter()
301 .any(|&f| f.eq_ignore_ascii_case(&field.name))
302 {
303 insertion_index = i + 1;
305 break;
306 }
307 }
308 }
309
310 insertion_index
311 }
312
313 pub fn remove(&mut self, name: &str) {
317 self.fields
318 .retain(|field| !field.name.eq_ignore_ascii_case(name));
319 }
320}
321
322impl std::fmt::Display for Field {
323 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
324 let lines = self.value.lines().collect::<Vec<_>>();
325 if lines.len() > 1 {
326 write!(f, "{}:", self.name)?;
327 for line in lines {
328 writeln!(f, " {}", line)?;
329 }
330 Ok(())
331 } else {
332 writeln!(f, "{}: {}", self.name, self.value)
333 }
334 }
335}
336
337impl std::fmt::Display for Paragraph {
338 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
339 for field in &self.fields {
340 field.fmt(f)?;
341 }
342 Ok(())
343 }
344}
345
346impl std::fmt::Display for Deb822 {
347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
348 for (i, paragraph) in self.0.iter().enumerate() {
349 if i > 0 {
350 writeln!(f)?;
351 }
352 write!(f, "{}", paragraph)?;
353 }
354 Ok(())
355 }
356}
357
358impl std::str::FromStr for Paragraph {
359 type Err = Error;
360
361 fn from_str(s: &str) -> Result<Self, Self::Err> {
362 let doc: Deb822 = s.parse()?;
363 if doc.is_empty() {
364 Err(Error::UnexpectedEof)
365 } else if doc.len() > 1 {
366 Err(Error::ExpectedEof)
367 } else {
368 Ok(doc.0.into_iter().next().unwrap())
369 }
370 }
371}
372
373impl From<Vec<(String, String)>> for Paragraph {
374 fn from(fields: Vec<(String, String)>) -> Self {
375 fields.into_iter().collect()
376 }
377}
378
379impl FromIterator<(String, String)> for Paragraph {
380 fn from_iter<T: IntoIterator<Item = (String, String)>>(iter: T) -> Self {
381 let fields = iter
382 .into_iter()
383 .map(|(name, value)| Field { name, value })
384 .collect();
385 Paragraph { fields }
386 }
387}
388
389impl IntoIterator for Paragraph {
390 type Item = (String, String);
391 type IntoIter = std::iter::Map<std::vec::IntoIter<Field>, fn(Field) -> (String, String)>;
392
393 fn into_iter(self) -> Self::IntoIter {
394 self.fields
395 .into_iter()
396 .map(|field| (field.name, field.value))
397 }
398}
399
400#[derive(Debug, PartialEq, Eq, Clone)]
402pub struct Deb822(Vec<Paragraph>);
403
404impl From<Deb822> for Vec<Paragraph> {
405 fn from(doc: Deb822) -> Self {
406 doc.0
407 }
408}
409
410impl IntoIterator for Deb822 {
411 type Item = Paragraph;
412 type IntoIter = std::vec::IntoIter<Paragraph>;
413
414 fn into_iter(self) -> Self::IntoIter {
415 self.0.into_iter()
416 }
417}
418
419impl Deb822 {
420 pub fn len(&self) -> usize {
422 self.0.len()
423 }
424
425 pub fn is_empty(&self) -> bool {
427 self.0.is_empty()
428 }
429
430 pub fn iter(&self) -> impl Iterator<Item = &Paragraph> {
432 self.0.iter()
433 }
434
435 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Paragraph> {
437 self.0.iter_mut()
438 }
439
440 pub fn from_reader<R: std::io::Read>(mut r: R) -> Result<Self, Error> {
442 let mut buf = String::new();
443 r.read_to_string(&mut buf)?;
444 buf.parse()
445 }
446
447 pub fn iter_paragraphs_from_reader<R: std::io::BufRead>(reader: R) -> ParagraphReader<R> {
452 ParagraphReader::new(reader)
453 }
454}
455
456pub struct ParagraphReader<R: std::io::BufRead> {
458 reader: R,
459 buffer: String,
460 finished: bool,
461}
462
463impl<R: std::io::BufRead> ParagraphReader<R> {
464 pub fn new(reader: R) -> Self {
466 Self {
467 reader,
468 buffer: String::new(),
469 finished: false,
470 }
471 }
472}
473
474impl<R: std::io::BufRead> Iterator for ParagraphReader<R> {
475 type Item = Result<Paragraph, Error>;
476
477 fn next(&mut self) -> Option<Self::Item> {
478 if self.finished {
479 return None;
480 }
481
482 self.buffer.clear();
483 let mut found_content = false;
484
485 loop {
486 let mut line = String::new();
487 match self.reader.read_line(&mut line) {
488 Ok(0) => {
489 self.finished = true;
491 if found_content {
492 return Some(self.buffer.parse());
494 }
495 return None;
496 }
497 Ok(_) => {
498 if line.trim().is_empty() && found_content {
500 return Some(self.buffer.parse());
502 }
503
504 if !found_content
506 && (line.trim().is_empty() || line.trim_start().starts_with('#'))
507 {
508 continue;
509 }
510
511 if !line.starts_with(|c: char| c.is_whitespace()) && line.contains(':') {
513 found_content = true;
514 } else if found_content {
515 } else if !line.trim_start().starts_with('#') {
517 found_content = true;
519 }
520
521 self.buffer.push_str(&line);
522 }
523 Err(e) => {
524 self.finished = true;
525 return Some(Err(Error::Io(e)));
526 }
527 }
528 }
529 }
530}
531
532impl std::str::FromStr for Deb822 {
533 type Err = Error;
534
535 fn from_str(s: &str) -> Result<Self, Self::Err> {
536 let bytes = s.as_bytes();
538 let mut paragraphs = Vec::new();
539 let mut pos = 0;
540 let len = bytes.len();
541
542 while pos < len {
543 while pos < len {
545 let b = bytes[pos];
546 if b == b'#' {
547 while pos < len && bytes[pos] != b'\n' {
548 pos += 1;
549 }
550 if pos < len {
551 pos += 1;
552 }
553 } else if b == b'\n' || b == b'\r' {
554 pos += 1;
555 } else {
556 break;
557 }
558 }
559
560 if pos >= len {
561 break;
562 }
563
564 if bytes[pos] == b' ' || bytes[pos] == b'\t' {
566 let line_start = pos;
567 while pos < len && bytes[pos] != b'\n' {
568 pos += 1;
569 }
570 let token = unsafe { std::str::from_utf8_unchecked(&bytes[line_start..pos]) };
571 return Err(Error::UnexpectedToken(token.to_string()));
572 }
573
574 let mut fields: Vec<Field> = Vec::new();
576
577 loop {
578 if pos >= len {
579 break;
580 }
581
582 if bytes[pos] == b'\n' {
584 pos += 1;
585 break;
586 }
587
588 if bytes[pos] == b'#' {
590 while pos < len && bytes[pos] != b'\n' {
591 pos += 1;
592 }
593 if pos < len {
594 pos += 1;
595 }
596 continue;
597 }
598
599 if bytes[pos] == b' ' || bytes[pos] == b'\t' {
601 if fields.is_empty() {
602 let line_start = pos;
604 while pos < len && bytes[pos] != b'\n' {
605 pos += 1;
606 }
607 let token =
608 unsafe { std::str::from_utf8_unchecked(&bytes[line_start..pos]) };
609 return Err(Error::UnexpectedToken(token.to_string()));
610 }
611
612 while pos < len && (bytes[pos] == b' ' || bytes[pos] == b'\t') {
614 pos += 1;
615 }
616
617 let line_start = pos;
619 while pos < len && bytes[pos] != b'\n' {
620 pos += 1;
621 }
622
623 if let Some(last_field) = fields.last_mut() {
625 last_field.value.push('\n');
626 last_field.value.push_str(unsafe {
627 std::str::from_utf8_unchecked(&bytes[line_start..pos])
628 });
629 }
630
631 if pos < len {
632 pos += 1; }
634 continue;
635 }
636
637 let name_start = pos;
639 while pos < len && bytes[pos] != b':' && bytes[pos] != b'\n' {
640 pos += 1;
641 }
642
643 if pos >= len || bytes[pos] != b':' {
644 let line_start = name_start;
646 while pos < len && bytes[pos] != b'\n' {
647 pos += 1;
648 }
649 let token = unsafe { std::str::from_utf8_unchecked(&bytes[line_start..pos]) };
650 return Err(Error::UnexpectedToken(token.to_string()));
651 }
652
653 let name = unsafe { std::str::from_utf8_unchecked(&bytes[name_start..pos]) };
654
655 if name.is_empty() {
657 let line_start = name_start;
658 let mut end = pos;
659 while end < len && bytes[end] != b'\n' {
660 end += 1;
661 }
662 let token = unsafe { std::str::from_utf8_unchecked(&bytes[line_start..end]) };
663 return Err(Error::UnexpectedToken(token.to_string()));
664 }
665
666 pos += 1; while pos < len && (bytes[pos] == b' ' || bytes[pos] == b'\t') {
670 pos += 1;
671 }
672
673 let value_start = pos;
675 while pos < len && bytes[pos] != b'\n' {
676 pos += 1;
677 }
678
679 let value = unsafe { std::str::from_utf8_unchecked(&bytes[value_start..pos]) };
680
681 fields.push(Field {
682 name: name.to_string(),
683 value: value.to_string(),
684 });
685
686 if pos < len {
687 pos += 1; }
689 }
690
691 if !fields.is_empty() {
692 paragraphs.push(Paragraph { fields });
693 }
694 }
695
696 Ok(Deb822(paragraphs))
697 }
698}
699
700#[cfg(test)]
701mod tests {
702 use super::*;
703
704 #[test]
705 fn test_error_display() {
706 let err = Error::UnexpectedToken("invalid".to_string());
707 assert_eq!(err.to_string(), "Unexpected token: invalid");
708
709 let err = Error::UnexpectedEof;
710 assert_eq!(err.to_string(), "Unexpected end-of-file");
711
712 let err = Error::ExpectedEof;
713 assert_eq!(err.to_string(), "Expected end-of-file");
714
715 let io_err = std::io::Error::other("test error");
716 let err = Error::Io(io_err);
717 assert!(err.to_string().contains("IO error: test error"));
718 }
719
720 #[test]
721 fn test_parse() {
722 let input = r#"Package: hello
723Version: 2.10
724Description: A program that says hello
725 Some more text
726
727Package: world
728Version: 1.0
729Description: A program that says world
730 And some more text
731Another-Field: value
732
733# A comment
734
735"#;
736
737 let mut deb822: Deb822 = input.parse().unwrap();
738 assert_eq!(
739 deb822,
740 Deb822(vec![
741 Paragraph {
742 fields: vec![
743 Field {
744 name: "Package".to_string(),
745 value: "hello".to_string(),
746 },
747 Field {
748 name: "Version".to_string(),
749 value: "2.10".to_string(),
750 },
751 Field {
752 name: "Description".to_string(),
753 value: "A program that says hello\nSome more text".to_string(),
754 },
755 ],
756 },
757 Paragraph {
758 fields: vec![
759 Field {
760 name: "Package".to_string(),
761 value: "world".to_string(),
762 },
763 Field {
764 name: "Version".to_string(),
765 value: "1.0".to_string(),
766 },
767 Field {
768 name: "Description".to_string(),
769 value: "A program that says world\nAnd some more text".to_string(),
770 },
771 Field {
772 name: "Another-Field".to_string(),
773 value: "value".to_string(),
774 },
775 ],
776 },
777 ])
778 );
779 assert_eq!(deb822.len(), 2);
780 assert!(!deb822.is_empty());
781 assert_eq!(deb822.iter().count(), 2);
782
783 let para = deb822.iter().next().unwrap();
784 assert_eq!(para.get("Package"), Some("hello"));
785 assert_eq!(para.get("Version"), Some("2.10"));
786 assert_eq!(
787 para.get("Description"),
788 Some("A program that says hello\nSome more text")
789 );
790 assert_eq!(para.get("Another-Field"), None);
791 assert!(!para.is_empty());
792 assert_eq!(para.len(), 3);
793 assert_eq!(
794 para.iter().collect::<Vec<_>>(),
795 vec![
796 ("Package", "hello"),
797 ("Version", "2.10"),
798 ("Description", "A program that says hello\nSome more text"),
799 ]
800 );
801 let para = deb822.iter_mut().next().unwrap();
802 para.insert("Another-Field", "value");
803 assert_eq!(para.get("Another-Field"), Some("value"));
804
805 let mut newpara = Paragraph { fields: vec![] };
806 newpara.insert("Package", "new");
807 assert_eq!(newpara.to_string(), "Package: new\n");
808 }
809
810 #[test]
811 fn test_paragraph_iter() {
812 let input = r#"Package: hello
813Version: 2.10
814"#;
815 let para: Paragraph = input.parse().unwrap();
816 let mut iter = para.into_iter();
817 assert_eq!(
818 iter.next(),
819 Some(("Package".to_string(), "hello".to_string()))
820 );
821 assert_eq!(
822 iter.next(),
823 Some(("Version".to_string(), "2.10".to_string()))
824 );
825 assert_eq!(iter.next(), None);
826 }
827
828 #[test]
829 fn test_format_multiline() {
830 let para = Paragraph {
831 fields: vec![Field {
832 name: "Description".to_string(),
833 value: "A program that says hello\nSome more text".to_string(),
834 }],
835 };
836
837 assert_eq!(
838 para.to_string(),
839 "Description: A program that says hello\n Some more text\n"
840 );
841 }
842
843 #[test]
844 fn test_paragraph_from_str_errors() {
845 let result = "Package: foo\n\nPackage: bar\n".parse::<Paragraph>();
847 assert!(matches!(result, Err(Error::ExpectedEof)));
848
849 let result = "".parse::<Paragraph>();
851 assert!(matches!(result, Err(Error::UnexpectedEof)));
852 }
853
854 #[test]
855 fn test_from_vec() {
856 let fields = vec![
857 ("Package".to_string(), "hello".to_string()),
858 ("Version".to_string(), "1.0".to_string()),
859 ];
860
861 let para: Paragraph = fields.into();
862 assert_eq!(para.get("Package"), Some("hello"));
863 assert_eq!(para.get("Version"), Some("1.0"));
864 }
865
866 #[test]
867 fn test_unexpected_tokens() {
868 let input = "Value before key\nPackage: hello\n";
870 let result = input.parse::<Deb822>();
871 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
872
873 let input = "Package hello\n";
875 let result = input.parse::<Deb822>();
876 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
877
878 let input = " Indented: value\n";
880 let result = input.parse::<Deb822>();
881 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
882
883 let input = "Key: value\nvalue without key\n";
885 let result = input.parse::<Deb822>();
886 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
887
888 let input = "Key: value\n:\n";
890 let result = input.parse::<Deb822>();
891 assert!(matches!(result, Err(Error::UnexpectedToken(_))));
892 }
893
894 #[test]
895 fn test_parse_continuation_with_colon() {
896 let input = "Package: test\nDescription: short\n line: with colon\n";
898 let result = input.parse::<Deb822>();
899 assert!(result.is_ok());
900
901 let deb822 = result.unwrap();
902 assert_eq!(deb822.0.len(), 1);
903 assert_eq!(deb822.0[0].fields.len(), 2);
904 assert_eq!(deb822.0[0].fields[0].name, "Package");
905 assert_eq!(deb822.0[0].fields[0].value, "test");
906 assert_eq!(deb822.0[0].fields[1].name, "Description");
907 assert_eq!(deb822.0[0].fields[1].value, "short\nline: with colon");
908 }
909
910 #[test]
911 fn test_parse_continuation_starting_with_colon() {
912 let input = "Package: test\nDescription: short\n :value\n";
914 let result = input.parse::<Deb822>();
915 assert!(result.is_ok());
916
917 let deb822 = result.unwrap();
918 assert_eq!(deb822.0.len(), 1);
919 assert_eq!(deb822.0[0].fields.len(), 2);
920 assert_eq!(deb822.0[0].fields[0].name, "Package");
921 assert_eq!(deb822.0[0].fields[0].value, "test");
922 assert_eq!(deb822.0[0].fields[1].name, "Description");
923 assert_eq!(deb822.0[0].fields[1].value, "short\n:value");
924 }
925
926 #[test]
927 fn test_from_reader() {
928 let input = "Package: hello\nVersion: 1.0\n";
930 let result = Deb822::from_reader(input.as_bytes()).unwrap();
931 assert_eq!(result.len(), 1);
932 let para = result.iter().next().unwrap();
933 assert_eq!(para.get("Package"), Some("hello"));
934
935 use std::io::Error as IoError;
937 struct FailingReader;
938 impl std::io::Read for FailingReader {
939 fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
940 Err(IoError::other("test error"))
941 }
942 }
943
944 let result = Deb822::from_reader(FailingReader);
945 assert!(matches!(result, Err(Error::Io(_))));
946 }
947
948 #[test]
949 fn test_deb822_vec_conversion() {
950 let paragraphs = vec![
951 Paragraph {
952 fields: vec![Field {
953 name: "Package".to_string(),
954 value: "hello".to_string(),
955 }],
956 },
957 Paragraph {
958 fields: vec![Field {
959 name: "Package".to_string(),
960 value: "world".to_string(),
961 }],
962 },
963 ];
964
965 let deb822 = Deb822(paragraphs.clone());
966 let vec: Vec<Paragraph> = deb822.into();
967 assert_eq!(vec, paragraphs);
968 }
969
970 #[test]
971 fn test_deb822_iteration() {
972 let paragraphs = vec![
973 Paragraph {
974 fields: vec![Field {
975 name: "Package".to_string(),
976 value: "hello".to_string(),
977 }],
978 },
979 Paragraph {
980 fields: vec![Field {
981 name: "Package".to_string(),
982 value: "world".to_string(),
983 }],
984 },
985 ];
986
987 let deb822 = Deb822(paragraphs.clone());
988
989 let collected: Vec<_> = deb822.into_iter().collect();
991 assert_eq!(collected, paragraphs);
992
993 let deb822 = Deb822(paragraphs.clone());
995 let iter_refs: Vec<&Paragraph> = deb822.iter().collect();
996 assert_eq!(iter_refs.len(), 2);
997 assert_eq!(iter_refs[0].get("Package"), Some("hello"));
998
999 let mut deb822 = Deb822(paragraphs.clone());
1000 for para in deb822.iter_mut() {
1001 if para.get("Package") == Some("hello") {
1002 para.set("Version", "1.0");
1003 }
1004 }
1005 assert_eq!(deb822.iter().next().unwrap().get("Version"), Some("1.0"));
1006 }
1007
1008 #[test]
1009 fn test_empty_collections() {
1010 let deb822 = Deb822(vec![]);
1012 assert!(deb822.is_empty());
1013 assert_eq!(deb822.len(), 0);
1014 assert_eq!(deb822.iter().count(), 0);
1015
1016 let para = Paragraph { fields: vec![] };
1018 assert!(para.is_empty());
1019 assert_eq!(para.len(), 0);
1020 assert_eq!(para.iter().count(), 0);
1021 assert_eq!(para.get("Any"), None);
1022
1023 assert_eq!(para.to_string(), "");
1025
1026 assert_eq!(deb822.to_string(), "");
1028 }
1029
1030 #[test]
1031 fn test_paragraph_mutable_iteration() {
1032 let mut para = Paragraph {
1033 fields: vec![
1034 Field {
1035 name: "First".to_string(),
1036 value: "1".to_string(),
1037 },
1038 Field {
1039 name: "Second".to_string(),
1040 value: "2".to_string(),
1041 },
1042 ],
1043 };
1044
1045 for (_, value) in para.iter_mut() {
1047 *value = format!("{}0", value);
1048 }
1049
1050 assert_eq!(para.get("First"), Some("10"));
1051 assert_eq!(para.get("Second"), Some("20"));
1052 }
1053
1054 #[test]
1055 fn test_insert_duplicate_key() {
1056 let mut para = Paragraph {
1057 fields: vec![Field {
1058 name: "Key".to_string(),
1059 value: "Value1".to_string(),
1060 }],
1061 };
1062
1063 para.insert("Key", "Value2");
1065
1066 assert_eq!(para.fields.len(), 2);
1067 assert_eq!(para.fields[0].value, "Value1");
1068 assert_eq!(para.fields[1].value, "Value2");
1069
1070 assert_eq!(para.get("Key"), Some("Value1"));
1072 }
1073
1074 #[test]
1075 fn test_multiline_field_format() {
1076 let field = Field {
1078 name: "MultiField".to_string(),
1079 value: "line1\nline2\nline3".to_string(),
1080 };
1081
1082 let formatted = format!("{}", field);
1083 assert_eq!(formatted, "MultiField: line1\n line2\n line3\n");
1084
1085 let para = Paragraph {
1087 fields: vec![field],
1088 };
1089
1090 let formatted = format!("{}", para);
1091 assert_eq!(formatted, "MultiField: line1\n line2\n line3\n");
1092 }
1093
1094 #[test]
1095 fn test_paragraph_parsing_edge_cases() {
1096 let input = "Key:\n";
1098 let para: Paragraph = input.parse().unwrap();
1099 assert_eq!(para.get("Key"), Some(""));
1100
1101 let input = "Key: \n";
1104 let para: Paragraph = input.parse().unwrap();
1105 assert_eq!(para.get("Key"), Some(""));
1106
1107 let input = "Key1: value1\n\n\n\nKey2: value2\n";
1109 let deb822: Deb822 = input.parse().unwrap();
1110 assert_eq!(deb822.len(), 2);
1111
1112 let input = "Key: value\n with\n indentation\n levels\n";
1115 let para: Paragraph = input.parse().unwrap();
1116 assert_eq!(para.get("Key"), Some("value\nwith\nindentation\nlevels"));
1117 }
1118
1119 #[test]
1120 fn test_parse_complex() {
1121 let input = "# Comment at start\nKey1: val1\nKey2: \n indented\nKey3: val3\n\n# Comment between paragraphs\n\nKey4: val4\n";
1123 let deb822: Deb822 = input.parse().unwrap();
1124
1125 assert_eq!(deb822.len(), 2);
1126 let paragraphs: Vec<Paragraph> = deb822.into();
1127
1128 assert_eq!(paragraphs[0].get("Key2"), Some("\nindented"));
1129 assert_eq!(paragraphs[1].get("Key4"), Some("val4"));
1130
1131 let input = "Key:\n indented value\n";
1133 let para: Paragraph = input.parse().unwrap();
1134 assert_eq!(para.get("Key"), Some("\nindented value"));
1135 }
1136
1137 #[test]
1138 fn test_deb822_display() {
1139 let para1 = Paragraph {
1141 fields: vec![Field {
1142 name: "Key1".to_string(),
1143 value: "Value1".to_string(),
1144 }],
1145 };
1146
1147 let para2 = Paragraph {
1148 fields: vec![Field {
1149 name: "Key2".to_string(),
1150 value: "Value2".to_string(),
1151 }],
1152 };
1153
1154 let deb822 = Deb822(vec![para1, para2]);
1155 let formatted = format!("{}", deb822);
1156
1157 assert_eq!(formatted, "Key1: Value1\n\nKey2: Value2\n");
1158 }
1159
1160 #[test]
1161 fn test_parser_edge_cases() {
1162 let input = "# Comment\nKey: value";
1166 let deb822: Deb822 = input.parse().unwrap();
1167 assert_eq!(deb822.len(), 1);
1168
1169 let input = "Key: value\n .indented";
1171 let deb822: Deb822 = input.parse().unwrap();
1172 assert_eq!(
1173 deb822.iter().next().unwrap().get("Key"),
1174 Some("value\n.indented")
1175 );
1176
1177 let input = "Key: value\n line1\n line2\n\nNextKey: value";
1179 let deb822: Deb822 = input.parse().unwrap();
1180 assert_eq!(deb822.len(), 2);
1181 assert_eq!(
1182 deb822.iter().next().unwrap().get("Key"),
1183 Some("value\nline1\nline2")
1184 );
1185 }
1186
1187 #[test]
1188 fn test_iter_paragraphs_from_reader() {
1189 use std::io::BufReader;
1190
1191 let input = r#"Package: hello
1192Version: 2.10
1193Description: A program that says hello
1194 Some more text
1195
1196Package: world
1197Version: 1.0
1198Description: A program that says world
1199 And some more text
1200Another-Field: value
1201
1202# A comment
1203
1204"#;
1205
1206 let reader = BufReader::new(input.as_bytes());
1207 let paragraphs: Result<Vec<_>, _> = Deb822::iter_paragraphs_from_reader(reader).collect();
1208 let paragraphs = paragraphs.unwrap();
1209
1210 assert_eq!(paragraphs.len(), 2);
1211
1212 assert_eq!(paragraphs[0].get("Package"), Some("hello"));
1213 assert_eq!(paragraphs[0].get("Version"), Some("2.10"));
1214 assert_eq!(
1215 paragraphs[0].get("Description"),
1216 Some("A program that says hello\nSome more text")
1217 );
1218
1219 assert_eq!(paragraphs[1].get("Package"), Some("world"));
1220 assert_eq!(paragraphs[1].get("Version"), Some("1.0"));
1221 assert_eq!(
1222 paragraphs[1].get("Description"),
1223 Some("A program that says world\nAnd some more text")
1224 );
1225 assert_eq!(paragraphs[1].get("Another-Field"), Some("value"));
1226 }
1227
1228 #[test]
1229 fn test_iter_paragraphs_from_reader_empty() {
1230 use std::io::BufReader;
1231
1232 let input = "";
1233 let reader = BufReader::new(input.as_bytes());
1234 let paragraphs: Result<Vec<_>, _> = Deb822::iter_paragraphs_from_reader(reader).collect();
1235 let paragraphs = paragraphs.unwrap();
1236
1237 assert_eq!(paragraphs.len(), 0);
1238 }
1239
1240 #[test]
1241 fn test_iter_paragraphs_from_reader_with_leading_comments() {
1242 use std::io::BufReader;
1243
1244 let input = r#"# Leading comment
1245# Another comment
1246
1247Package: test
1248Version: 1.0
1249"#;
1250
1251 let reader = BufReader::new(input.as_bytes());
1252 let paragraphs: Result<Vec<_>, _> = Deb822::iter_paragraphs_from_reader(reader).collect();
1253 let paragraphs = paragraphs.unwrap();
1254
1255 assert_eq!(paragraphs.len(), 1);
1256 assert_eq!(paragraphs[0].get("Package"), Some("test"));
1257 }
1258
1259 #[test]
1260 fn test_case_insensitive_get() {
1261 let para = Paragraph {
1262 fields: vec![
1263 Field {
1264 name: "Package".to_string(),
1265 value: "test".to_string(),
1266 },
1267 Field {
1268 name: "Version".to_string(),
1269 value: "1.0".to_string(),
1270 },
1271 ],
1272 };
1273
1274 assert_eq!(para.get("Package"), Some("test"));
1276 assert_eq!(para.get("package"), Some("test"));
1277 assert_eq!(para.get("PACKAGE"), Some("test"));
1278 assert_eq!(para.get("PaCkAgE"), Some("test"));
1279
1280 assert_eq!(para.get("Version"), Some("1.0"));
1281 assert_eq!(para.get("version"), Some("1.0"));
1282 assert_eq!(para.get("VERSION"), Some("1.0"));
1283 }
1284
1285 #[test]
1286 fn test_case_insensitive_set() {
1287 let mut para = Paragraph {
1288 fields: vec![Field {
1289 name: "Package".to_string(),
1290 value: "test".to_string(),
1291 }],
1292 };
1293
1294 para.set("package", "updated");
1296 assert_eq!(para.fields.len(), 1);
1297 assert_eq!(para.get("Package"), Some("updated"));
1298 assert_eq!(para.get("package"), Some("updated"));
1299
1300 para.set("PACKAGE", "updated2");
1302 assert_eq!(para.fields.len(), 1);
1303 assert_eq!(para.get("Package"), Some("updated2"));
1304 }
1305
1306 #[test]
1307 fn test_case_insensitive_remove() {
1308 let mut para = Paragraph {
1309 fields: vec![
1310 Field {
1311 name: "Package".to_string(),
1312 value: "test".to_string(),
1313 },
1314 Field {
1315 name: "Version".to_string(),
1316 value: "1.0".to_string(),
1317 },
1318 ],
1319 };
1320
1321 para.remove("package");
1323 assert_eq!(para.fields.len(), 1);
1324 assert_eq!(para.get("Package"), None);
1325 assert_eq!(para.get("Version"), Some("1.0"));
1326
1327 para.remove("VERSION");
1329 assert_eq!(para.fields.len(), 0);
1330 assert_eq!(para.get("Version"), None);
1331 }
1332
1333 #[test]
1334 fn test_case_preservation() {
1335 let mut para = Paragraph { fields: vec![] };
1336
1337 para.insert("Package", "test");
1339 assert_eq!(para.fields[0].name, "Package");
1340
1341 para.set("package", "updated");
1343 assert_eq!(para.fields[0].name, "Package");
1344 assert_eq!(para.fields[0].value, "updated");
1345 }
1346}