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
use std::collections::BTreeMap;
use std::fmt;

use crate::action::Action;
use crate::feature::Feature;
use crate::Provide;
use crate::Require;

/// A collection of features, indexed by `(feature_name, feature_version)`.
#[derive(Debug, Clone)]
pub struct FeatureSet {
    features: BTreeMap<(&'static str, u64), Feature>,
}

impl FeatureSet {
    /// Create a new `FeatureSet` from a sequence of actions that add or remove a provided feature.
    pub fn from_provides<'a>(provides: impl IntoIterator<Item = &'a Action<Provide>>) -> Self {
        let mut features = BTreeMap::new();
        for a in provides {
            match a {
                Action::Add(p) => {
                    let feature = p.feature();
                    features.insert((feature.name(), feature.ver()), feature.clone());
                }
                Action::Delete(p) => {
                    let feature = p.feature();
                    features.remove(&(feature.name(), feature.ver()));
                }
            }
        }

        Self { features }
    }

    /// Create a new `FeatureSet` from a sequence of actions that add or remove a required feature.
    ///
    /// If `include_optional` is `true`, then optional features are included in the resulting
    /// `FeatureSet`.
    pub fn from_required<'a>(
        required: impl IntoIterator<Item = &'a Action<Require>>,
        include_optional: bool,
    ) -> Self {
        let mut features = BTreeMap::new();
        for a in required {
            match a {
                Action::Add(p) => {
                    if !include_optional && p.optional() {
                        continue;
                    }
                    let feature = p.feature();
                    features.insert((feature.name(), feature.ver()), feature.clone());
                }
                Action::Delete(p) => {
                    let feature = p.feature();
                    features.remove(&(feature.name(), feature.ver()));
                }
            }
        }

        Self { features }
    }

    pub fn contains(&self, name_ver: (&str, u64)) -> bool {
        self.features.contains_key(&name_ver)
    }
}

impl fmt::Display for FeatureSet {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "{}",
            self.features
                .keys()
                .map(|(n, v)| { format!("{}:v{:}", n, v) })
                .collect::<Vec<_>>()
                .join(", ")
        )
    }
}