blob: a676450d9ab9e5078189885e91f8e6324d5586d8 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::HashMap;
use std::sync::Mutex;
use anyhow::bail;
use anyhow::Context;
use anyhow::Result;
#[cfg(feature = "chromeos")]
use featured::CheckFeature;
use once_cell::sync::OnceCell; // Trait CheckFeature is for is_feature_enabled_blocking
// Only use featured in ebuild as using featured makes "cargo build" fail.
//
// Reference: https://chromium.googlesource.com/chromiumos/platform2/+/main/featured/README.md
struct FeatureManager {
// Contains the cached results of feature queries.
feature_caches: HashMap<String, bool>,
// There must only ever be one struct instance for a given feature name.
//
// Reference: https://chromium.googlesource.com/chromiumos/platform2/+/79195b9779a292e50cef56b609ea089bd92f2175/featured/c_feature_library.h#25
#[cfg(feature = "chromeos")]
features: HashMap<String, featured::Feature>,
}
impl FeatureManager {
fn new() -> FeatureManager {
FeatureManager {
feature_caches: HashMap::new(),
#[cfg(feature = "chromeos")]
features: HashMap::new(),
}
}
// Returns the cached feature query result.
fn is_feature_enabled(&self, feature_name: &str) -> Result<bool> {
match self.feature_caches.get(feature_name) {
Some(value) => Ok(*value),
None => Ok(false),
}
}
// Adds a feature to the hashmap if it's not present and caches the feature query.
fn update_feature(&mut self, feature_name: &str) -> Result<()> {
#[cfg(feature = "chromeos")]
if !self.features.contains_key(feature_name) {
let feature = featured::Feature::new(feature_name, false)?;
self.features.insert(feature_name.to_string(), feature);
}
#[cfg(feature = "chromeos")]
let feature_enabled = featured::PlatformFeatures::get()?.is_feature_enabled_blocking(
self.features
.get(feature_name)
.context("No feature entry in HashMap")?,
);
#[cfg(not(feature = "chromeos"))]
let feature_enabled = false;
self.feature_caches
.insert(feature_name.to_string(), feature_enabled);
Ok(())
}
}
// FeatureManager is thread safe because its content is protected by a Mutex.
unsafe impl Send for FeatureManager {}
// Singleton pattern.
static FEATURE_MANAGER: OnceCell<Mutex<FeatureManager>> = OnceCell::new();
pub fn init() -> Result<()> {
if FEATURE_MANAGER
.set(Mutex::new(FeatureManager::new()))
.is_err()
{
bail!("Failed to set FEATURE_MANAGER");
}
Ok(())
}
pub fn is_feature_enabled(feature_name: &str) -> Result<bool> {
let feature_manager = FEATURE_MANAGER
.get()
.context("FEATURE_MANAGER is not initialized")?;
if let Ok(feature_manager_lock) = feature_manager.lock() {
feature_manager_lock.is_feature_enabled(feature_name)
} else {
bail!("Failed to lock FEATURE_MANAGER");
}
}
pub fn update_feature(feature_name: &str) -> Result<()> {
let feature_manager = FEATURE_MANAGER
.get()
.context("FEATURE_MANAGER is not initialized")?;
if let Ok(mut feature_manager_lock) = feature_manager.lock() {
feature_manager_lock.update_feature(feature_name)
} else {
bail!("Failed to lock FEATURE_MANAGER");
}
}