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
// 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/.

use crate::package::Package;
use std::collections::BTreeMap;

/// A target describes what platform and configuration we're trying
/// to deploy on.
#[derive(Clone, Debug, Default)]
pub struct Target(pub BTreeMap<String, String>);

impl Target {
    // Returns true if this target should include the package.
    pub(crate) fn includes_package(&self, pkg: &Package) -> bool {
        let valid_targets = if let Some(targets) = &pkg.only_for_targets {
            // If targets are specified for the packages, filter them.
            targets
        } else {
            // If no targets are specified, assume the package should be
            // included by default.
            return true;
        };

        // For each of the targets permitted by the package, check if
        // the current target matches.
        for (k, v) in valid_targets {
            let target_value = if let Some(target_value) = self.0.get(k) {
                target_value
            } else {
                return false;
            };

            if target_value != v {
                return false;
            };
        }
        true
    }
}

impl std::fmt::Display for Target {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        for (key, value) in &self.0 {
            write!(f, "{}={} ", key, value)?;
        }
        Ok(())
    }
}

#[derive(thiserror::Error, Debug)]
pub enum TargetParseError {
    #[error("Cannot parse key-value pair out of '{0}'")]
    MissingEquals(String),
}

impl std::str::FromStr for Target {
    type Err = TargetParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let kvs = s
            .split_whitespace()
            .map(|kv| {
                kv.split_once('=')
                    .ok_or_else(|| TargetParseError::MissingEquals(kv.to_string()))
                    .map(|(k, v)| (k.to_string(), v.to_string()))
            })
            .collect::<Result<BTreeMap<String, String>, _>>()?;
        Ok(Target(kvs))
    }
}