settings/
base.rs

1// Copyright 2020 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
5//! Service-wide definitions.
6//!
7//! # Summary
8//!
9//! The base mod houses the core definitions for communicating information
10//! across the service. Note that there are currently references to types in
11//! other nested base mods. It is the long-term intention that the common
12//! general (non-domain specific or overarching) definitions are migrated here,
13//! while particular types, such as setting-specific definitions, are moved to
14//! a common base mod underneath the parent setting mod.
15
16use crate::accessibility::types::AccessibilityInfo;
17use crate::audio::types::AudioInfo;
18use crate::display::types::DisplayInfo;
19use crate::do_not_disturb::types::DoNotDisturbInfo;
20use crate::factory_reset::types::FactoryResetInfo;
21use crate::ingress::fidl;
22use crate::input::types::InputInfo;
23#[cfg(test)]
24use serde::Deserialize;
25use serde::Serialize;
26use std::collections::HashSet;
27
28/// The setting types supported by the service.
29#[derive(PartialEq, Debug, Eq, Hash, Clone, Copy, Serialize)]
30pub enum SettingType {
31    /// This value is reserved for testing purposes.
32    #[cfg(test)]
33    Unknown,
34    Accessibility,
35    Audio,
36    Display,
37    DoNotDisturb,
38    FactoryReset,
39    Input,
40    Intl,
41    Keyboard,
42    Light,
43    NightMode,
44    Privacy,
45    Setup,
46}
47
48/// [Entity] defines the types of components that exist within the setting service. Entities can be
49/// any part of the system that can be interacted with. Others can reference [Entities](Entity) to
50/// declare associations, such as dependencies.
51#[derive(PartialEq, Debug, Eq, Hash, Clone, Copy)]
52pub enum Entity {
53    /// A component that handles requests for the specified [SettingType].
54    Handler(SettingType),
55}
56
57/// A [Dependency] declares a reliance of a particular configuration/feature/component/etc. within
58/// the setting service. [Dependencies](Dependency) are used to generate the necessary component map
59/// to support a particular service configuration. It can used to determine if the platform/product
60/// configuration can support the requested service configuration.
61#[derive(PartialEq, Debug, Eq, Hash, Clone, Copy)]
62pub(crate) enum Dependency {
63    /// An [Entity] is a component within the setting service.
64    Entity(Entity),
65}
66
67impl Dependency {
68    /// Returns whether the [Dependency] can be handled by the provided environment. Currently, this
69    /// only involves [SettingType] handlers.
70    pub(crate) fn is_fulfilled(&self, entities: &HashSet<Entity>) -> bool {
71        match self {
72            Dependency::Entity(entity) => entities.contains(entity),
73        }
74    }
75}
76
77/// This macro takes an enum, which has variants associated with exactly one data, and
78/// generates the same enum and implements a for_inspect method.
79/// The for_inspect method returns variants' names and formated data contents.
80#[macro_export]
81macro_rules! generate_inspect_with_info {
82    ($(#[$metas:meta])* pub enum $name:ident {
83        $(
84            $(#[doc = $str:expr])*
85            $(#[cfg($test:meta)])?
86            $variant:ident ( $data:ty )
87        ),* $(,)?
88    }
89    ) => {
90        $(#[$metas])*
91        pub enum $name {
92            $(
93                $(#[doc = $str])*
94                $(#[cfg($test)])?
95                $variant($data),
96            )*
97        }
98
99        impl $name {
100            /// Returns the name of the enum and its value, debug-formatted, for writing to inspect.
101            pub(crate) fn for_inspect(&self) -> (&'static str, String) {
102                match self {
103                    $(
104                        $(#[cfg($test)])?
105                        $name::$variant(info) => (stringify!($variant), format!("{:?}", info)),
106                    )*
107                }
108            }
109        }
110    };
111}
112
113generate_inspect_with_info! {
114    /// Enumeration over the possible info types available in the service.
115    #[derive(PartialEq, Debug, Clone)]
116    pub enum SettingInfo {
117        /// This value is reserved for testing purposes.
118        #[cfg(test)]
119        Unknown(UnknownInfo),
120        Accessibility(AccessibilityInfo),
121        Audio(AudioInfo),
122        Brightness(DisplayInfo),
123        FactoryReset(FactoryResetInfo),
124        DoNotDisturb(DoNotDisturbInfo),
125        Input(InputInfo),
126    }
127}
128
129pub(crate) trait HasSettingType {
130    const SETTING_TYPE: SettingType;
131}
132
133macro_rules! conversion_impls {
134    ($($(#[cfg($test:meta)])? $variant:ident($info_ty:ty) => $ty_variant:ident ),+ $(,)?) => {
135        $(
136            $(#[cfg($test)])?
137            impl HasSettingType for $info_ty {
138                const SETTING_TYPE: SettingType = SettingType::$ty_variant;
139            }
140
141            $(#[cfg($test)])?
142            impl TryFrom<SettingInfo> for $info_ty {
143                type Error = ();
144
145                fn try_from(setting_info: SettingInfo) -> Result<Self, ()> {
146                    match setting_info {
147                        SettingInfo::$variant(info) => Ok(info),
148                        _ => Err(()),
149                    }
150                }
151            }
152        )+
153    }
154}
155
156conversion_impls! {
157    #[cfg(test)] Unknown(UnknownInfo) => Unknown,
158    Accessibility(AccessibilityInfo) => Accessibility,
159    Audio(AudioInfo) => Audio,
160    Brightness(DisplayInfo) => Display,
161    FactoryReset(FactoryResetInfo) => FactoryReset,
162    DoNotDisturb(DoNotDisturbInfo) => DoNotDisturb,
163    Input(InputInfo) => Input,
164}
165
166impl From<&SettingInfo> for SettingType {
167    fn from(info: &SettingInfo) -> SettingType {
168        match info {
169            #[cfg(test)]
170            SettingInfo::Unknown(_) => SettingType::Unknown,
171            SettingInfo::Accessibility(_) => SettingType::Accessibility,
172            SettingInfo::Audio(_) => SettingType::Audio,
173            SettingInfo::Brightness(_) => SettingType::Display,
174            SettingInfo::DoNotDisturb(_) => SettingType::DoNotDisturb,
175            SettingInfo::FactoryReset(_) => SettingType::FactoryReset,
176            SettingInfo::Input(_) => SettingType::Input,
177        }
178    }
179}
180
181/// This struct is reserved for testing purposes. Some tests need to verify data changes, bool value
182/// can be used for this purpose.
183#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
184#[cfg(test)]
185#[derive(Default)]
186pub struct UnknownInfo(pub bool);
187
188/// The `Merge` trait allows merging two structs.
189pub(crate) trait Merge<Other = Self> {
190    /// Returns a copy of the original struct where the values of all fields set in `other`
191    /// replace the matching fields in the copy of `self`.
192    fn merge(&self, other: Other) -> Self;
193}
194
195/// Returns the default interfaces supported by any product if none are supplied.
196pub fn get_default_interfaces() -> HashSet<fidl::InterfaceSpec> {
197    [
198        fidl::InterfaceSpec::Accessibility,
199        fidl::InterfaceSpec::Intl,
200        fidl::InterfaceSpec::Privacy,
201        fidl::InterfaceSpec::Setup,
202    ]
203    .into()
204}
205
206/// Returns all known setting types. New additions to SettingType should also
207/// be inserted here.
208#[cfg(test)]
209pub(crate) fn get_all_setting_types() -> HashSet<SettingType> {
210    [
211        SettingType::Accessibility,
212        SettingType::Audio,
213        SettingType::Display,
214        SettingType::DoNotDisturb,
215        SettingType::FactoryReset,
216        SettingType::Input,
217        SettingType::Intl,
218        SettingType::Keyboard,
219        SettingType::Light,
220        SettingType::NightMode,
221        SettingType::Privacy,
222        SettingType::Setup,
223    ]
224    .into()
225}
226
227#[cfg(test)]
228mod testing {
229    use settings_storage::device_storage::DeviceStorageCompatible;
230    use settings_storage::storage_factory::NoneT;
231
232    use super::{SettingInfo, UnknownInfo};
233
234    impl DeviceStorageCompatible for UnknownInfo {
235        type Loader = NoneT;
236        const KEY: &'static str = "unknown_info";
237    }
238
239    impl From<UnknownInfo> for SettingInfo {
240        fn from(info: UnknownInfo) -> SettingInfo {
241            SettingInfo::Unknown(info)
242        }
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[allow(clippy::bool_assert_comparison)]
251    #[fuchsia::test]
252    fn test_dependency_fulfillment() {
253        let target_entity = Entity::Handler(SettingType::Unknown);
254        let dependency = Dependency::Entity(target_entity);
255        let mut available_entities = HashSet::new();
256
257        // Verify that an empty entity set does not fulfill dependency.
258        assert_eq!(dependency.is_fulfilled(&available_entities), false);
259
260        // Verify an entity set without the target entity does not fulfill dependency.
261        let _ = available_entities.insert(Entity::Handler(SettingType::FactoryReset));
262        assert_eq!(dependency.is_fulfilled(&available_entities), false);
263
264        // Verify an entity set with target entity does fulfill dependency.
265        let _ = available_entities.insert(target_entity);
266        assert!(dependency.is_fulfilled(&available_entities));
267    }
268}