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(())
    }
}