Skip to main content

debian_control/lossy/
apt.rs

1//! APT related structures
2use crate::lossy::{Relations, SourceRelation};
3use deb822_fast::{FromDeb822, FromDeb822Paragraph, ToDeb822, ToDeb822Paragraph};
4
5fn deserialize_yesno(s: &str) -> Result<bool, String> {
6    match s {
7        "yes" => Ok(true),
8        "no" => Ok(false),
9        _ => Err(format!("invalid value for yesno: {}", s)),
10    }
11}
12
13fn serialize_yesno(b: &bool) -> String {
14    if *b {
15        "yes".to_string()
16    } else {
17        "no".to_string()
18    }
19}
20
21fn deserialize_components(value: &str) -> Result<Vec<String>, String> {
22    Ok(value.split_whitespace().map(|s| s.to_string()).collect())
23}
24
25fn join_whitespace(components: &[String]) -> String {
26    components.join(" ")
27}
28
29fn deserialize_architectures(value: &str) -> Result<Vec<String>, String> {
30    Ok(value.split_whitespace().map(|s| s.to_string()).collect())
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, ToDeb822, FromDeb822)]
34/// A Release file
35pub struct Release {
36    #[deb822(field = "Codename")]
37    /// The codename of the release
38    pub codename: Option<String>,
39
40    #[deb822(
41        field = "Components",
42        deserialize_with = deserialize_components,
43        serialize_with = join_whitespace
44    )]
45    /// Components supported by the release
46    pub components: Vec<String>,
47
48    #[deb822(
49        field = "Architectures",
50        deserialize_with = deserialize_architectures,
51        serialize_with = join_whitespace
52    )]
53    /// Architectures supported by the release
54    pub architectures: Vec<String>,
55
56    #[deb822(field = "Description")]
57    /// Description of the release
58    pub description: Option<String>,
59
60    #[deb822(field = "Origin")]
61    /// Origin of the release
62    pub origin: Option<String>,
63
64    #[deb822(field = "Label")]
65    /// Label of the release
66    pub label: Option<String>,
67
68    #[deb822(field = "Suite")]
69    /// Suite of the release
70    pub suite: Option<String>,
71
72    #[deb822(field = "Version")]
73    /// Version of the release
74    pub version: Option<String>,
75
76    #[deb822(field = "Date")]
77    /// Date the release was published
78    pub date: Option<String>,
79
80    #[deb822(field = "NotAutomatic", deserialize_with = deserialize_yesno, serialize_with = serialize_yesno)]
81    /// Whether the release is not automatic
82    pub not_automatic: Option<bool>,
83
84    #[deb822(field = "ButAutomaticUpgrades", deserialize_with = deserialize_yesno, serialize_with = serialize_yesno)]
85    /// Indicates if packages retrieved from this release should be automatically upgraded
86    pub but_automatic_upgrades: Option<bool>,
87
88    #[deb822(field = "Acquire-By-Hash", deserialize_with = deserialize_yesno, serialize_with = serialize_yesno)]
89    /// Whether packages files can be acquired by hash
90    pub acquire_by_hash: Option<bool>,
91
92    #[deb822(field = "MD5Sum", deserialize_with = deserialize_checksums::<crate::fields::Md5Checksum>, serialize_with = serialize_checksums::<crate::fields::Md5Checksum>)]
93    /// MD5 checksums of repository index files
94    pub checksums_md5: Option<Vec<crate::fields::Md5Checksum>>,
95
96    #[deb822(field = "SHA1", deserialize_with = deserialize_checksums::<crate::fields::Sha1Checksum>, serialize_with = serialize_checksums::<crate::fields::Sha1Checksum>)]
97    /// SHA-1 checksums of repository index files
98    pub checksums_sha1: Option<Vec<crate::fields::Sha1Checksum>>,
99
100    #[deb822(field = "SHA256", deserialize_with = deserialize_checksums::<crate::fields::Sha256Checksum>, serialize_with = serialize_checksums::<crate::fields::Sha256Checksum>)]
101    /// SHA-256 checksums of repository index files
102    pub checksums_sha256: Option<Vec<crate::fields::Sha256Checksum>>,
103
104    #[deb822(field = "SHA512", deserialize_with = deserialize_checksums::<crate::fields::Sha512Checksum>, serialize_with = serialize_checksums::<crate::fields::Sha512Checksum>)]
105    /// SHA-512 checksums of repository index files
106    pub checksums_sha512: Option<Vec<crate::fields::Sha512Checksum>>,
107}
108
109impl std::str::FromStr for Release {
110    type Err = String;
111
112    fn from_str(s: &str) -> Result<Self, Self::Err> {
113        let para = s
114            .parse::<deb822_fast::Paragraph>()
115            .map_err(|e| e.to_string())?;
116        FromDeb822Paragraph::from_paragraph(&para)
117    }
118}
119
120impl std::fmt::Display for Release {
121    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
122        let para: deb822_fast::Paragraph = self.to_paragraph();
123        write!(f, "{}", para)
124    }
125}
126
127fn deserialize_binaries(value: &str) -> Result<Vec<String>, String> {
128    Ok(value.split(",").map(|s| s.trim().to_string()).collect())
129}
130
131fn deserialize_testsuite_triggers(value: &str) -> Result<Vec<String>, String> {
132    Ok(value.split(",").map(|s| s.trim().to_string()).collect())
133}
134
135fn join_lines(components: &[String]) -> String {
136    components.join("\n")
137}
138
139fn deserialize_package_list(value: &str) -> Result<Vec<String>, String> {
140    Ok(value.split('\n').map(|s| s.to_string()).collect())
141}
142
143fn deserialize_checksums<T>(value: &str) -> Result<Vec<T>, String>
144where
145    T: std::str::FromStr,
146    T::Err: std::fmt::Display,
147{
148    value
149        .lines()
150        .map(|s| s.trim())
151        .filter(|s| !s.is_empty())
152        .map(|s| s.parse::<T>().map_err(|e| e.to_string()))
153        .collect()
154}
155
156fn serialize_checksums<T: std::fmt::Display>(checksums: &[T]) -> String {
157    checksums
158        .iter()
159        .map(|c| format!(" {}", c))
160        .collect::<Vec<_>>()
161        .join("\n")
162}
163
164#[derive(Debug, Clone, PartialEq, Eq, ToDeb822, FromDeb822)]
165/// A source
166pub struct Source {
167    #[deb822(field = "Directory")]
168    /// The directory of the source
169    pub directory: String,
170
171    #[deb822(field = "Description")]
172    /// Description of the source
173    pub description: Option<String>,
174
175    #[deb822(field = "Version")]
176    /// Version of the source
177    pub version: debversion::Version,
178
179    #[deb822(field = "Package")]
180    /// Package of the source
181    pub package: String,
182
183    #[deb822(field = "Binary", deserialize_with = deserialize_binaries, serialize_with = join_whitespace)]
184    /// Binaries of the source
185    pub binaries: Option<Vec<String>>,
186
187    #[deb822(field = "Maintainer")]
188    /// Maintainer of the source
189    pub maintainer: Option<String>,
190
191    #[deb822(field = "Build-Depends")]
192    /// Build dependencies of the source
193    pub build_depends: Option<Relations>,
194
195    #[deb822(field = "Build-Depends-Indep")]
196    /// Build dependencies independent of the architecture of the source
197    pub build_depends_indep: Option<Relations>,
198
199    #[deb822(field = "Build-Depends-Arch")]
200    /// Build dependencies dependent on the architecture of the source
201    pub build_depends_arch: Option<Relations>,
202
203    #[deb822(field = "Build-Conflicts")]
204    /// Build conflicts of the source
205    pub build_conflicts: Option<Relations>,
206
207    #[deb822(field = "Build-Conflicts-Indep")]
208    /// Build conflicts independent of the architecture of the source
209    pub build_conflicts_indep: Option<Relations>,
210
211    #[deb822(field = "Build-Conflicts-Arch")]
212    /// Build conflicts dependent on the architecture of the source
213    pub build_conflicts_arch: Option<Relations>,
214
215    #[deb822(field = "Standards-Version")]
216    /// Standards version of the source
217    pub standards_version: Option<String>,
218
219    #[deb822(field = "Homepage")]
220    /// Homepage of the source
221    pub homepage: Option<String>,
222
223    #[deb822(field = "Autobuild")]
224    /// Whether the source should be autobuilt
225    pub autobuild: Option<bool>,
226
227    #[deb822(field = "Testsuite")]
228    /// Testsuite of the source
229    pub testsuite: Option<String>,
230
231    #[deb822(field = "Testsuite-Triggers", deserialize_with = deserialize_testsuite_triggers, serialize_with = join_whitespace)]
232    /// The packages triggering the testsuite of the source
233    pub testsuite_triggers: Option<Vec<String>>,
234
235    #[deb822(field = "Vcs-Browser")]
236    /// VCS browser of the source
237    pub vcs_browser: Option<String>,
238
239    #[deb822(field = "Vcs-Git")]
240    /// VCS Git of the source
241    pub vcs_git: Option<String>,
242
243    #[deb822(field = "Vcs-Bzr")]
244    /// VCS Bzr of the source
245    pub vcs_bzr: Option<String>,
246
247    #[deb822(field = "Vcs-Hg")]
248    /// VCS Hg of the source
249    pub vcs_hg: Option<String>,
250
251    #[deb822(field = "Vcs-Svn")]
252    /// VCS SVN of the source
253    pub vcs_svn: Option<String>,
254
255    #[deb822(field = "Vcs-Darcs")]
256    /// VCS Darcs of the source
257    pub vcs_darcs: Option<String>,
258
259    #[deb822(field = "Vcs-Cvs")]
260    /// VCS CVS of the source
261    pub vcs_cvs: Option<String>,
262
263    #[deb822(field = "Vcs-Arch")]
264    /// VCS Arch of the source
265    pub vcs_arch: Option<String>,
266
267    #[deb822(field = "Vcs-Mtn")]
268    /// VCS Mtn of the source
269    pub vcs_mtn: Option<String>,
270
271    #[deb822(field = "Dgit")]
272    /// Dgit information (commit, suite, ref, url)
273    pub dgit: Option<crate::fields::DgitInfo>,
274
275    #[deb822(field = "Priority")]
276    /// Priority of the source
277    pub priority: Option<crate::fields::Priority>,
278
279    #[deb822(field = "Section")]
280    /// Section of the source
281    pub section: Option<String>,
282
283    #[deb822(field = "Format")]
284    /// Format of the source
285    pub format: Option<String>,
286
287    #[deb822(field = "Architecture")]
288    /// Architectures the source builds for
289    pub architecture: Option<String>,
290
291    #[deb822(field = "Package-List", deserialize_with = deserialize_package_list, serialize_with = join_lines)]
292    /// Package list of the source
293    pub package_list: Vec<String>,
294
295    #[deb822(field = "Files", deserialize_with = deserialize_checksums::<crate::fields::Md5Checksum>, serialize_with = serialize_checksums::<crate::fields::Md5Checksum>)]
296    /// MD5 checksums of source files
297    pub files: Option<Vec<crate::fields::Md5Checksum>>,
298
299    #[deb822(field = "Checksums-Sha1", deserialize_with = deserialize_checksums::<crate::fields::Sha1Checksum>, serialize_with = serialize_checksums::<crate::fields::Sha1Checksum>)]
300    /// SHA-1 checksums of source files
301    pub checksums_sha1: Option<Vec<crate::fields::Sha1Checksum>>,
302
303    #[deb822(field = "Checksums-Sha256", deserialize_with = deserialize_checksums::<crate::fields::Sha256Checksum>, serialize_with = serialize_checksums::<crate::fields::Sha256Checksum>)]
304    /// SHA-256 checksums of source files
305    pub checksums_sha256: Option<Vec<crate::fields::Sha256Checksum>>,
306
307    #[deb822(field = "Checksums-Sha512", deserialize_with = deserialize_checksums::<crate::fields::Sha512Checksum>, serialize_with = serialize_checksums::<crate::fields::Sha512Checksum>)]
308    /// SHA-512 checksums of source files
309    pub checksums_sha512: Option<Vec<crate::fields::Sha512Checksum>>,
310}
311
312impl std::str::FromStr for Source {
313    type Err = String;
314
315    fn from_str(s: &str) -> Result<Self, Self::Err> {
316        let para = s
317            .parse::<deb822_fast::Paragraph>()
318            .map_err(|e| e.to_string())?;
319
320        FromDeb822Paragraph::from_paragraph(&para)
321    }
322}
323
324impl std::fmt::Display for Source {
325    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
326        let para: deb822_fast::Paragraph = self.to_paragraph();
327        write!(f, "{}", para)
328    }
329}
330
331/// A package
332#[derive(Debug, Clone, PartialEq, Eq, ToDeb822, FromDeb822)]
333pub struct Package {
334    /// The name of the package
335    #[deb822(field = "Package")]
336    pub name: String,
337
338    /// The version of the package
339    #[deb822(field = "Version")]
340    pub version: debversion::Version,
341
342    /// The name and version of the source package, if different from `name`
343    #[deb822(field = "Source")]
344    pub source: Option<SourceRelation>,
345
346    /// The architecture of the package
347    #[deb822(field = "Architecture")]
348    pub architecture: String,
349
350    /// The maintainer of the package
351    #[deb822(field = "Maintainer")]
352    pub maintainer: Option<String>,
353
354    /// The installed size of the package
355    #[deb822(field = "Installed-Size")]
356    pub installed_size: Option<usize>,
357
358    /// Dependencies
359    #[deb822(field = "Depends")]
360    pub depends: Option<Relations>,
361
362    /// Pre-Depends
363    #[deb822(field = "Pre-Depends")]
364    pub pre_depends: Option<Relations>,
365
366    /// Recommends
367    #[deb822(field = "Recommends")]
368    pub recommends: Option<Relations>,
369
370    /// Suggests
371    #[deb822(field = "Suggests")]
372    pub suggests: Option<Relations>,
373
374    /// Enhances
375    #[deb822(field = "Enhances")]
376    pub enhances: Option<Relations>,
377
378    /// Breaks
379    #[deb822(field = "Breaks")]
380    pub breaks: Option<Relations>,
381
382    /// Conflicts
383    #[deb822(field = "Conflicts")]
384    pub conflicts: Option<Relations>,
385
386    /// Provides
387    #[deb822(field = "Provides")]
388    pub provides: Option<Relations>,
389
390    /// Replaces
391    #[deb822(field = "Replaces")]
392    pub replaces: Option<Relations>,
393
394    /// Built-Using
395    #[deb822(field = "Built-Using")]
396    pub built_using: Option<Relations>,
397
398    /// Static-Built-Using
399    #[deb822(field = "Static-Built-Using")]
400    pub static_built_using: Option<Relations>,
401
402    /// Description
403    #[deb822(field = "Description")]
404    pub description: Option<String>,
405
406    /// Homepage
407    #[deb822(field = "Homepage")]
408    pub homepage: Option<String>,
409
410    /// Priority
411    #[deb822(field = "Priority")]
412    pub priority: Option<crate::fields::Priority>,
413
414    /// Section
415    #[deb822(field = "Section")]
416    pub section: Option<String>,
417
418    /// Essential
419    #[deb822(field = "Essential", deserialize_with = deserialize_yesno, serialize_with = serialize_yesno)]
420    pub essential: Option<bool>,
421
422    /// Tag
423    #[deb822(field = "Tag")]
424    pub tag: Option<String>,
425
426    /// Size
427    #[deb822(field = "Size")]
428    pub size: Option<usize>,
429
430    /// Filename (path within the repository)
431    #[deb822(field = "Filename")]
432    pub filename: Option<String>,
433
434    /// MD5sum
435    #[deb822(field = "MD5sum")]
436    pub md5sum: Option<String>,
437
438    /// SHA1
439    #[deb822(field = "SHA1")]
440    pub sha1: Option<String>,
441
442    /// SHA256
443    #[deb822(field = "SHA256")]
444    pub sha256: Option<String>,
445
446    /// SHA512
447    #[deb822(field = "SHA512")]
448    pub sha512: Option<String>,
449
450    /// Description (MD5)
451    #[deb822(field = "Description-MD5")]
452    pub description_md5: Option<String>,
453}
454
455impl std::str::FromStr for Package {
456    type Err = String;
457
458    fn from_str(s: &str) -> Result<Self, Self::Err> {
459        let para = s
460            .parse::<deb822_fast::Paragraph>()
461            .map_err(|e| e.to_string())?;
462
463        FromDeb822Paragraph::from_paragraph(&para)
464    }
465}
466
467impl std::fmt::Display for Package {
468    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
469        let para: deb822_fast::Paragraph = self.to_paragraph();
470        write!(f, "{}", para)
471    }
472}
473
474#[cfg(test)]
475mod tests {
476    use super::*;
477    use deb822_fast::Paragraph;
478    use deb822_fast::ToDeb822Paragraph;
479
480    #[test]
481    fn test_release() {
482        let release = Release {
483            codename: Some("focal".to_string()),
484            components: vec!["main".to_string(), "restricted".to_string()],
485            architectures: vec!["amd64".to_string(), "arm64".to_string()],
486            description: Some("Ubuntu 20.04 LTS".to_string()),
487            origin: Some("Ubuntu".to_string()),
488            label: Some("Ubuntu".to_string()),
489            suite: Some("focal".to_string()),
490            version: Some("20.04".to_string()),
491            date: Some("Thu, 23 Apr 2020 17:19:19 UTC".to_string()),
492            not_automatic: Some(false),
493            but_automatic_upgrades: Some(true),
494            acquire_by_hash: Some(true),
495            checksums_md5: None,
496            checksums_sha1: None,
497            checksums_sha256: None,
498            checksums_sha512: None,
499        };
500
501        let deb822 = r#"Codename: focal
502Components: main restricted
503Architectures: amd64 arm64
504Description: Ubuntu 20.04 LTS
505Origin: Ubuntu
506Label: Ubuntu
507Suite: focal
508Version: 20.04
509Date: Thu, 23 Apr 2020 17:19:19 UTC
510NotAutomatic: no
511ButAutomaticUpgrades: yes
512Acquire-By-Hash: yes
513"#;
514
515        let para = deb822.parse::<Paragraph>().unwrap();
516
517        let release: deb822_fast::Paragraph = release.to_paragraph();
518
519        assert_eq!(release, para);
520    }
521
522    #[test]
523    fn test_package() {
524        let package = r#"Package: apt
525Version: 2.1.10
526Architecture: amd64
527Maintainer: APT Development Team <apt@lists.debian.org>
528Installed-Size: 3524
529Depends: libc6 (>= 2.14), libgcc1
530Pre-Depends: dpkg (>= 1.15.6)
531Recommends: gnupg
532Suggests: apt-doc, aptitude | synaptic | wajig
533"#;
534
535        let package: Package = package.parse().unwrap();
536
537        assert_eq!(package.name, "apt");
538        assert_eq!(package.version, "2.1.10".parse().unwrap());
539        assert_eq!(package.architecture, "amd64");
540    }
541
542    #[test]
543    fn test_package_essential() {
544        let package = r#"Package: base-files
545Version: 11.1
546Architecture: amd64
547Essential: yes
548"#;
549
550        let package: Package = package.parse().unwrap();
551
552        assert_eq!(package.name, "base-files");
553        assert_eq!(package.essential, Some(true));
554    }
555
556    #[test]
557    fn test_package_essential_no() {
558        let package = r#"Package: apt
559Version: 2.1.10
560Architecture: amd64
561Essential: no
562"#;
563
564        let package: Package = package.parse().unwrap();
565
566        assert_eq!(package.name, "apt");
567        assert_eq!(package.essential, Some(false));
568    }
569
570    #[test]
571    fn test_package_with_different_source() {
572        let package = r#"Package: apt
573Source: not-apt (1.1.5)
574Version: 2.1.10
575Architecture: amd64
576Maintainer: APT Development Team <apt@lists.debian.org>
577Installed-Size: 3524
578Depends: libc6 (>= 2.14), libgcc1
579Pre-Depends: dpkg (>= 1.15.6)
580Recommends: gnupg
581Suggests: apt-doc, aptitude | synaptic | wajig
582"#;
583
584        let package: Package = package.parse().unwrap();
585
586        assert_eq!(package.name, "apt");
587        assert_eq!(package.version, "2.1.10".parse().unwrap());
588        assert_eq!(package.architecture, "amd64");
589        assert_eq!(
590            package.source,
591            Some(SourceRelation {
592                name: "not-apt".to_string(),
593                version: Some("1.1.5".parse().unwrap())
594            })
595        );
596    }
597
598    #[test]
599    fn test_source() {
600        let source = r#"Package: abinit
601Binary: abinit, abinit-doc, abinit-data
602Version: 9.10.4-3
603Maintainer: Debichem Team <debichem-devel@lists.alioth.debian.org>
604Uploaders: Andreas Tille <tille@debian.org>, Michael Banck <mbanck@debian.org>
605Build-Depends: debhelper (>= 11), gfortran, liblapack-dev, python3, graphviz, markdown, ghostscript, help2man, libfftw3-dev, libhdf5-dev, libnetcdff-dev, libssl-dev, libxc-dev, mpi-default-dev, python3-dev, python3-numpy, python3-pandas, python3-yaml, texlive-latex-extra, texlive-fonts-recommended, texlive-extra-utils, texlive-pstricks, texlive-publishers, texlive-luatex
606Architecture: any all
607Standards-Version: 3.9.8
608Format: 3.0 (quilt)
609Files:
610 843550cbd14395c0b9408158a91a239c 2464 abinit_9.10.4-3.dsc
611 a323f11fbd4a7d0f461d99c931903b5c 130747285 abinit_9.10.4.orig.tar.gz
612 27c12d3dac5cd105cebaa2af4247e807 15068 abinit_9.10.4-3.debian.tar.xz
613Vcs-Browser: https://salsa.debian.org/debichem-team/abinit
614Vcs-Git: https://salsa.debian.org/debichem-team/abinit.git
615Checksums-Sha256:
616 c3c217b14bc5705a1d8930a2e7fcef58e64beaa22abc213e2eacc7d5537ef840 2464 abinit_9.10.4-3.dsc
617 6bf3c276c333956f722761f189f2b4324e150c8a50470ecb72ee07cc1c457d48 130747285 abinit_9.10.4.orig.tar.gz
618 80c4fb7575d67f3167d7c34fd59477baf839810d0b863e19f1dd9fea1bc0b3b5 15068 abinit_9.10.4-3.debian.tar.xz
619Homepage: http://www.abinit.org/
620Package-List: 
621 abinit deb science optional arch=any
622 abinit-data deb science optional arch=all
623 abinit-doc deb doc optional arch=all
624Testsuite: autopkgtest
625Testsuite-Triggers: python3, python3-numpy, python3-pandas, python3-yaml
626Directory: pool/main/a/abinit
627Priority: optional
628Section: science
629"#;
630
631        let source: Source = source.parse().unwrap();
632
633        assert_eq!(source.package, "abinit");
634        assert_eq!(source.version, "9.10.4-3".parse().unwrap());
635        assert_eq!(
636            source.binaries,
637            Some(vec![
638                "abinit".to_string(),
639                "abinit-doc".to_string(),
640                "abinit-data".to_string()
641            ])
642        );
643
644        let build_depends = source.build_depends.as_ref();
645        let build_depends: Vec<_> = build_depends.iter().collect();
646        let build_depends = build_depends[0];
647
648        let expected_build_depends = &[
649            "debhelper",
650            "gfortran",
651            "liblapack-dev",
652            "python3",
653            "graphviz",
654            "markdown",
655            "ghostscript",
656            "help2man",
657            "libfftw3-dev",
658            "libhdf5-dev",
659            "libnetcdff-dev",
660            "libssl-dev",
661            "libxc-dev",
662            "mpi-default-dev",
663            "python3-dev",
664            "python3-numpy",
665            "python3-pandas",
666            "python3-yaml",
667            "texlive-latex-extra",
668            "texlive-fonts-recommended",
669            "texlive-extra-utils",
670            "texlive-pstricks",
671            "texlive-publishers",
672            "texlive-luatex",
673        ];
674
675        assert_eq!(build_depends.len(), expected_build_depends.len());
676        assert_eq!(build_depends[0][0].name, expected_build_depends[0]);
677        assert_eq!(
678            build_depends[build_depends.len() - 1][0].name,
679            expected_build_depends[build_depends.len() - 1]
680        );
681
682        assert_eq!(
683            source.testsuite_triggers,
684            Some(
685                ["python3", "python3-numpy", "python3-pandas", "python3-yaml"]
686                    .into_iter()
687                    .map(String::from)
688                    .collect()
689            )
690        );
691    }
692
693    #[test]
694    fn test_source_with_dgit() {
695        let source = r#"Package: test-pkg
696Version: 1.0-1
697Maintainer: Test Maintainer <test@example.com>
698Dgit: c1370424e2404d3c22bd09c828d4b28d81d897ad debian archive/debian/1.1.0 https://git.dgit.debian.org/test-pkg
699Directory: pool/main/t/test-pkg
700Package-List: 
701 abinit deb science optional arch=any
702 abinit-data deb science optional arch=all
703 abinit-doc deb doc optional arch=all
704"#;
705
706        let source: Source = source.parse().unwrap();
707        assert_eq!(source.package, "test-pkg");
708        let dgit = source.dgit.as_ref().unwrap();
709        assert_eq!(dgit.commit, "c1370424e2404d3c22bd09c828d4b28d81d897ad");
710        assert_eq!(dgit.suite, "debian");
711        assert_eq!(dgit.git_ref, "archive/debian/1.1.0");
712        assert_eq!(dgit.url, "https://git.dgit.debian.org/test-pkg");
713    }
714
715    #[test]
716    fn test_source_checksums() {
717        let source = r#"Package: hello
718Version: 1.0-1
719Directory: pool/main/h/hello
720Package-List: 
721 hello deb misc optional arch=any
722Files:
723 acbd18db4cc2f85cedef654fccc4a4d8 1234 hello_1.0.orig.tar.gz
724Checksums-Sha256:
725 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 1234 hello_1.0.orig.tar.gz
726"#;
727
728        let source: Source = source.parse().unwrap();
729        assert_eq!(source.package, "hello");
730        let files = source.files.as_ref().unwrap();
731        assert_eq!(files.len(), 1);
732        assert_eq!(files[0].md5sum, "acbd18db4cc2f85cedef654fccc4a4d8");
733        assert_eq!(files[0].size, 1234);
734        assert_eq!(files[0].filename, "hello_1.0.orig.tar.gz");
735        let sha256 = source.checksums_sha256.as_ref().unwrap();
736        assert_eq!(sha256.len(), 1);
737        assert_eq!(
738            sha256[0].sha256,
739            "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
740        );
741    }
742
743    #[test]
744    fn test_release_checksums() {
745        let release_str = r#"Suite: stable
746Codename: stable
747Architectures: amd64
748Components: main
749MD5Sum:
750 acbd18db4cc2f85cedef654fccc4a4d8 1234 main/binary-amd64/Packages
751SHA256:
752 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 1234 main/binary-amd64/Packages
753"#;
754
755        let release: Release = release_str.parse().unwrap();
756        assert_eq!(release.suite, Some("stable".to_string()));
757        let md5 = release.checksums_md5.as_ref().unwrap();
758        assert_eq!(md5.len(), 1);
759        assert_eq!(md5[0].filename, "main/binary-amd64/Packages");
760        assert_eq!(md5[0].size, 1234);
761        let sha256 = release.checksums_sha256.as_ref().unwrap();
762        assert_eq!(sha256.len(), 1);
763        assert_eq!(sha256[0].filename, "main/binary-amd64/Packages");
764    }
765}