1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

/*! Interface with a collection of binary package control definitions. */

use {
    crate::binary_package_control::BinaryPackageControlFile,
    std::ops::{Deref, DerefMut},
};

/// Represents a collection of binary package control files.
///
/// Various operations in Debian packaging operate against a collection of
/// binary package control files. For example, resolving dependencies of a
/// package requires finding packages from an available set. This type facilitates
/// the implementation of said operations.
#[derive(Clone, Debug, Default)]
pub struct BinaryPackageList<'a> {
    packages: Vec<BinaryPackageControlFile<'a>>,
}

impl<'a> Deref for BinaryPackageList<'a> {
    type Target = Vec<BinaryPackageControlFile<'a>>;

    fn deref(&self) -> &Self::Target {
        &self.packages
    }
}

impl<'a> DerefMut for BinaryPackageList<'a> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.packages
    }
}

impl<'a> IntoIterator for BinaryPackageList<'a> {
    type Item = BinaryPackageControlFile<'a>;
    type IntoIter = std::vec::IntoIter<Self::Item>;

    fn into_iter(self) -> Self::IntoIter {
        self.packages.into_iter()
    }
}

impl<'a> BinaryPackageList<'a> {
    /// Find instances of a package within this collection.
    pub fn find_packages_with_name(
        &self,
        package: String,
    ) -> impl Iterator<Item = &BinaryPackageControlFile<'a>> {
        self.packages
            .iter()
            .filter(move |cf| matches!(cf.package(), Ok(name) if name == package))
    }
}

#[cfg(test)]
mod test {
    use {
        super::*,
        crate::{control::ControlParagraphReader, error::Result},
        indoc::indoc,
        std::io::Cursor,
    };

    const FOO_1_2: &str = indoc! {"
        Package: foo
        Version: 1.2
        Installed-Size: 20268
        Architecture: amd64
    "};

    const BAR_1_0: &str = indoc! {"
        Package: bar
        Version: 1.0
        Architecture: amd64
        Depends: foo (>= 1.2)
    "};

    const BAZ_1_1: &str = indoc! {"
        Package: baz
        Version: 1.1
        Architecture: amd64
        Depends: bar (>= 1.0)
    "};

    #[test]
    fn find_package() -> Result<()> {
        let foo_para = ControlParagraphReader::new(Cursor::new(FOO_1_2.as_bytes()))
            .next()
            .unwrap()?;

        let bar_para = ControlParagraphReader::new(Cursor::new(BAR_1_0.as_bytes()))
            .next()
            .unwrap()?;

        let baz_para = ControlParagraphReader::new(Cursor::new(BAZ_1_1.as_bytes()))
            .next()
            .unwrap()?;

        let mut l = BinaryPackageList::default();
        l.push(BinaryPackageControlFile::from(foo_para));
        l.push(BinaryPackageControlFile::from(bar_para));
        l.push(BinaryPackageControlFile::from(baz_para));

        assert_eq!(l.find_packages_with_name("other".into()).count(), 0);

        let packages = l.find_packages_with_name("foo".into()).collect::<Vec<_>>();
        assert_eq!(packages.len(), 1);
        assert_eq!(packages[0].version_str()?, "1.2");

        let packages = l.find_packages_with_name("bar".into()).collect::<Vec<_>>();
        assert_eq!(packages.len(), 1);

        Ok(())
    }
}