cml/
features.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::error::Error;
6use std::fmt;
7use std::str::FromStr;
8
9/// Represents the set of features a CML file is compiled with. This struct can be
10/// used to check whether a feature used in CML is enabled.
11#[derive(Debug)]
12pub struct FeatureSet(Vec<Feature>);
13
14impl FeatureSet {
15    /// Create an empty FeatureSet.
16    pub const fn empty() -> FeatureSet {
17        FeatureSet(Vec::new())
18    }
19
20    /// Tests whether `feature` is enabled.
21    pub fn has(&self, feature: &Feature) -> bool {
22        self.0.iter().find(|f| *f == feature).is_some()
23    }
24
25    /// Returns an `Err` if `feature` is not enabled.
26    pub fn check(&self, feature: Feature) -> Result<(), Error> {
27        if self.has(&feature) { Ok(()) } else { Err(Error::RestrictedFeature(feature.to_string())) }
28    }
29}
30
31impl From<Vec<Feature>> for FeatureSet {
32    fn from(features: Vec<Feature>) -> FeatureSet {
33        FeatureSet(features)
34    }
35}
36
37/// A feature that can be enabled/opt-into.
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum Feature {
40    /// Allows `dictionary` capabilities to be used.
41    /// This is unused, and will be deleted in a followup
42    Dictionaries,
43
44    /// Allows `dictionary` capabilities with `extends: program/...` to be defined (a.k.a.
45    /// dynamic dictionaries)
46    DynamicDictionaries,
47
48    /// Allows `dictionary` use declarations.
49    UseDictionaries,
50
51    // Allows dynamic child name lengths to exceed the default limit.
52    AllowLongNames,
53
54    // Allow tests to resolve non-hermetic packages. This requires EnableAllowNonHermeticPackagesFeature
55    // to be enabled.
56    AllowNonHermeticPackages,
57
58    // Enable AllowNonHermeticPackages feature. This helps us to only enable
59    // this in-tree.
60    EnableAllowNonHermeticPackagesFeature,
61
62    // Restrict test types in facet. This helps us to only restrict this in-tree.
63    RestrictTestTypeInFacet,
64
65    // Allows customizing when the framework opens a capability when a consumer
66    // component requests to connect to the capability.
67    DeliveryType,
68}
69
70impl FromStr for Feature {
71    type Err = String;
72    fn from_str(s: &str) -> Result<Self, Self::Err> {
73        match s {
74            "dictionaries" => Ok(Feature::Dictionaries),
75            "dynamic_dictionaries" => Ok(Feature::DynamicDictionaries),
76            "use_dictionaries" => Ok(Feature::UseDictionaries),
77            "allow_long_names" => Ok(Feature::AllowLongNames),
78            "allow_non_hermetic_packages" => Ok(Feature::AllowNonHermeticPackages),
79            "enable_allow_non_hermetic_packages_feature" => {
80                Ok(Feature::EnableAllowNonHermeticPackagesFeature)
81            }
82            "restrict_test_type_in_facets" => Ok(Feature::RestrictTestTypeInFacet),
83            "delivery_type" => Ok(Feature::DeliveryType),
84            _ => Err(format!("unrecognized feature \"{}\"", s)),
85        }
86    }
87}
88
89impl fmt::Display for Feature {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        f.write_str(match self {
92            Feature::Dictionaries => "dictionaries",
93            Feature::DynamicDictionaries => "dynamic_dictionaries",
94            Feature::UseDictionaries => "use_dictionaries",
95            Feature::AllowLongNames => "allow_long_names",
96            Feature::AllowNonHermeticPackages => "allow_non_hermetic_packages",
97            Feature::EnableAllowNonHermeticPackagesFeature => {
98                "enable_allow_non_hermetic_packages_feature"
99            }
100            Feature::RestrictTestTypeInFacet => "restrict_test_type_in_facets",
101            Feature::DeliveryType => "delivery_type",
102        })
103    }
104}
105
106#[cfg(test)]
107mod tests {
108    use super::*;
109    use assert_matches::assert_matches;
110
111    #[test]
112    fn feature_is_parsed() {
113        assert_eq!(Feature::AllowLongNames, "allow_long_names".parse::<Feature>().unwrap());
114        assert_eq!(
115            Feature::AllowNonHermeticPackages,
116            "allow_non_hermetic_packages".parse::<Feature>().unwrap()
117        );
118    }
119
120    #[test]
121    fn feature_is_printed() {
122        assert_eq!("allow_long_names", Feature::AllowLongNames.to_string());
123        assert_eq!("allow_non_hermetic_packages", Feature::AllowNonHermeticPackages.to_string());
124        assert_eq!(
125            "enable_allow_non_hermetic_packages_feature",
126            Feature::EnableAllowNonHermeticPackagesFeature.to_string()
127        );
128        assert_eq!("restrict_test_type_in_facets", Feature::RestrictTestTypeInFacet.to_string());
129    }
130
131    #[test]
132    fn feature_set_has() {
133        let set = FeatureSet::empty();
134        assert!(!set.has(&Feature::AllowLongNames));
135
136        let set = FeatureSet::from(vec![Feature::AllowLongNames]);
137        assert!(set.has(&Feature::AllowLongNames));
138    }
139
140    #[test]
141    fn feature_set_check() {
142        let set = FeatureSet::empty();
143        assert_matches!(
144            set.check(Feature::AllowLongNames),
145            Err(Error::RestrictedFeature(f)) if f == "allow_long_names"
146        );
147
148        let set = FeatureSet::from(vec![Feature::AllowLongNames]);
149        assert_matches!(set.check(Feature::AllowLongNames), Ok(()));
150    }
151}