blob: 7efbbd19838719aa1d223594adfcb5d3f7114011 [file]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! **To define a base::Feature in Rust, use the `base_feature` macro.**
//! (Detailed information on usage can be found on the comment for the
//! macro itself, which is defined at the bottom of this file.)
use std::ffi::c_char;
use std::sync::atomic::AtomicU32;
#[doc(hidden)]
pub mod internal {
/// Secret handshake to (try to) ensure all places that construct a
/// base::Feature go through the helper `BASE_FEATURE()` macro.
pub enum FeatureMacroHandshake {
Secret,
}
}
#[repr(i32)]
#[derive(Clone, Copy)]
pub enum FeatureState {
Disabled = 0,
Enabled = 1,
}
/// Rust equivalent of the C++ `base::Feature` type.
///
/// This struct must maintain an identical memory layout to the C++ version
/// for FFI compatibility. Detailed documentation for feature flags can be
/// found in `base/feature.h`.
#[repr(C)]
pub struct Feature {
/// The name of the feature. Private to enforce null-termination via the
/// constructor.
name: &'static c_char,
/// The default state of the feature.
pub default_state: FeatureState,
/// Cached override state, used by C++ FeatureList::IsEnabled.
/// Initialized to 0.
cached_value: AtomicU32,
}
// LINT.IfChange(FeatureStruct)
const _: () = {
assert!(
std::mem::size_of::<Feature>()
== if std::mem::size_of::<*const ()>() == 8 { 16 } else { 12 }
);
assert!(std::mem::align_of::<Feature>() == std::mem::size_of::<*const ()>());
assert!(std::mem::offset_of!(Feature, name) == 0);
assert!(std::mem::offset_of!(Feature, default_state) == std::mem::size_of::<*const ()>());
assert!(std::mem::offset_of!(Feature, cached_value) == std::mem::size_of::<*const ()>() + 4);
};
// LINT.ThenChange(feature_list_unittest.cc:FeatureStruct)
impl Feature {
/// Create a new Feature definition where the name is derived from the Rust
/// identifier. Internal use only via the `base_feature!` macro.
///
/// # Safety
/// `name` must be a null-terminated string.
#[doc(hidden)]
pub const unsafe fn from_id(
name: &'static str,
default_state: FeatureState,
_handshake: internal::FeatureMacroHandshake,
) -> Self {
Self {
// Safety: name is guaranteed to be null-terminated.
// We take a pointer here.
name: unsafe { &*(name.as_ptr() as *const c_char) },
default_state,
cached_value: AtomicU32::new(0),
}
}
/// Check if the feature is enabled.
pub fn is_enabled(&self) -> bool {
ffi::FeatureList::IsEnabled(self.into())
}
}
impl<'a> From<&'a Feature> for &'a ffi::Feature {
fn from(feature: &'a Feature) -> Self {
// Safety: Feature is ABI-compatible with ffi::Feature (checked by static
// asserts above).
unsafe { std::mem::transmute(feature) }
}
}
// Safety: Feature is intended to be used as a global static and is thread-safe
// thanks to the atomic cached_value.
unsafe impl Sync for Feature {}
#[cxx::bridge(namespace = "base")]
mod ffi {
unsafe extern "C++" {
include!("base/feature.h");
include!("base/feature_list.h");
type Feature;
#[namespace = "base"]
type FeatureList;
#[Self = "FeatureList"]
fn IsEnabled(feature: &Feature) -> bool;
}
}
/// The macro for defining base::Features in Rust is `base_feature!`.
///
/// -`$id`is the Rust identifier that will be used for the Feature.
/// - `$default` is the default state to use for the feature. The options are
/// `feature::FeatureState::Disabled` or `feature::FeatureState::Enabled`.
///
/// # Usage:
///
/// use feature::{base_feature, FeatureState};
///
/// base_feature!(MyFeature, FeatureState::Disabled);
///
/// if MyFeature.is_enabled() {
/// // ...
/// }
///
/// Feature names are derived from the `$id` passed to the macro.
/// They should use CamelCase-style naming, e.g. "FooFeature".
///
/// Feature names must be globally unique.
///
/// (Note that features defined in C++ use the `k` prefix as an identifier,
/// meaning kFooFeature is the identifier in C++ code for what is processed
/// by Finch as FooFeature. Rust does not have this `k`-prefix requirement,
/// so a Feature declared in C++ with `kFooFeature` is referenced in Rust as
/// `FooFeature`, and both map to the same underlying Feature.)
#[macro_export]
macro_rules! base_feature {
// 2-argument version: Derive the name from the identifier.
($id:ident, $default:expr) => {
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals)]
pub static $id: $crate::Feature = unsafe {
// Safety: The string constructed here is explicitly null-terminated.
$crate::Feature::from_id(
concat!(stringify!($id), "\0"),
$default,
$crate::internal::FeatureMacroHandshake::Secret,
)
};
};
}