logiguard fork v3: full patch set on verified 8c74db0 tree

Includes prior-session patches (carry forward so the app compiles):
  - crates/gpui/build.rs: cross-compile manifest fix
  - crates/gpui/src/platform.rs: PlatformWindow::activate_with_token trait method
  - crates/gpui/src/window.rs: Window::activate_with_token public API
  - crates/gpui_linux/src/linux/wayland/window.rs: WaylandWindow::activate_with_token + activate() keyboard-serial fix

Plus the focus-serial fix:
  - serial.rs: SerialKind::KeyboardEnter
  - client.rs: store wl_keyboard.enter serial; latest_serial_of()

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mohamad Khani
2026-07-14 01:52:12 +03:30
commit b9819977a5
3984 changed files with 1487015 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
[package]
name = "feature_flags"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/feature_flags.rs"
[dependencies]
collections.workspace = true
feature_flags_macros.workspace = true
fs.workspace = true
gpui.workspace = true
inventory.workspace = true
schemars.workspace = true
serde_json.workspace = true
settings.workspace = true
[dev-dependencies]
gpui = { workspace = true, features = ["test-support"] }
settings = { workspace = true, features = ["test-support"] }

View File

@@ -0,0 +1 @@
../../LICENSE-GPL

View File

@@ -0,0 +1,287 @@
// Makes the derive macro's reference to `::feature_flags::FeatureFlagValue`
// resolve when the macro is invoked inside this crate itself.
extern crate self as feature_flags;
mod flags;
mod settings;
mod store;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::LazyLock;
use gpui::{App, Context, Global, Subscription, Window};
pub use feature_flags_macros::EnumFeatureFlag;
pub use flags::*;
pub use settings::{FeatureFlagsSettings, generate_feature_flags_schema};
pub use store::*;
pub static ZED_DISABLE_STAFF: LazyLock<bool> = LazyLock::new(|| {
std::env::var("ZED_DISABLE_STAFF").is_ok_and(|value| !value.is_empty() && value != "0")
});
impl Global for FeatureFlagStore {}
pub trait FeatureFlagValue:
Sized + Clone + Eq + Default + std::fmt::Debug + Send + Sync + 'static
{
/// Every possible value for this flag, in the order the UI should display them.
fn all_variants() -> &'static [Self];
/// A stable identifier for this variant used when persisting overrides.
fn override_key(&self) -> &'static str;
fn from_wire(wire: &str) -> Option<Self>;
/// Human-readable label for use in the configuration UI.
fn label(&self) -> &'static str {
self.override_key()
}
/// The variant that represents "on" — what the store resolves to when
/// staff rules, `enabled_for_all`, or a server announcement apply.
///
/// For enum flags this is usually the same as [`Default::default`] (the
/// variant marked `#[default]` in the derive). [`PresenceFlag`] overrides
/// this so that `default() == Off` (the "unconfigured" state) but
/// `on_variant() == On` (the "enabled" state).
fn on_variant() -> Self {
Self::default()
}
}
/// Default value type for simple on/off feature flags.
///
/// The fallback value is [`PresenceFlag::Off`] so that an absent / unknown
/// flag reads as disabled; the `on_variant` override pins the "enabled"
/// state to [`PresenceFlag::On`] so staff / server / `enabled_for_all`
/// resolution still lights the flag up.
#[derive(Clone, Copy, Default, PartialEq, Eq, Debug)]
pub enum PresenceFlag {
On,
#[default]
Off,
}
/// Presence flags deref to a `bool` so call sites can use `if *flag` without
/// spelling out the enum variant — or pass them anywhere a `&bool` is wanted.
impl std::ops::Deref for PresenceFlag {
type Target = bool;
fn deref(&self) -> &bool {
match self {
PresenceFlag::On => &true,
PresenceFlag::Off => &false,
}
}
}
impl FeatureFlagValue for PresenceFlag {
fn all_variants() -> &'static [Self] {
&[PresenceFlag::On, PresenceFlag::Off]
}
fn override_key(&self) -> &'static str {
match self {
PresenceFlag::On => "on",
PresenceFlag::Off => "off",
}
}
fn label(&self) -> &'static str {
match self {
PresenceFlag::On => "On",
PresenceFlag::Off => "Off",
}
}
fn from_wire(_: &str) -> Option<Self> {
Some(PresenceFlag::On)
}
fn on_variant() -> Self {
PresenceFlag::On
}
}
/// To create a feature flag, implement this trait on a trivial type and use it as
/// a generic parameter when called [`FeatureFlagAppExt::has_flag`].
///
/// Feature flags are enabled for members of Zed staff by default. To disable this behavior
/// so you can test flags being disabled, set ZED_DISABLE_STAFF=1 in your environment,
/// which will force Zed to treat the current user as non-staff.
pub trait FeatureFlag {
const NAME: &'static str;
/// The type of value this flag can hold. Use [`PresenceFlag`] for simple
/// on/off flags.
type Value: FeatureFlagValue;
/// Returns whether this feature flag is enabled for Zed staff.
fn enabled_for_staff() -> bool {
true
}
/// Returns whether this feature flag is enabled for everyone.
///
/// This is generally done on the server, but we provide this as a way to entirely enable a feature flag client-side
/// without needing to remove all of the call sites.
fn enabled_for_all() -> bool {
false
}
/// Subscribes the current view to changes in the feature flag store, so
/// that any mutation of flags or overrides will trigger a re-render.
///
/// The returned subscription is immediately detached; use [`observe_flag`]
/// directly if you need to hold onto the subscription.
fn watch<V: 'static>(cx: &mut Context<V>) {
cx.observe_global::<FeatureFlagStore>(|_, cx| cx.notify())
.detach();
}
}
pub trait FeatureFlagViewExt<V: 'static> {
/// Fires the callback whenever the resolved [`T::Value`] transitions.
fn observe_flag<T: FeatureFlag, F>(&mut self, window: &Window, callback: F) -> Subscription
where
F: Fn(T::Value, &mut V, &mut Window, &mut Context<V>) + Send + Sync + 'static;
fn when_flag_enabled<T: FeatureFlag>(
&mut self,
window: &mut Window,
callback: impl Fn(&mut V, &mut Window, &mut Context<V>) + Send + Sync + 'static,
);
}
impl<V> FeatureFlagViewExt<V> for Context<'_, V>
where
V: 'static,
{
fn observe_flag<T: FeatureFlag, F>(&mut self, window: &Window, callback: F) -> Subscription
where
F: Fn(T::Value, &mut V, &mut Window, &mut Context<V>) + 'static,
{
let mut last_value: Option<T::Value> = None;
self.observe_global_in::<FeatureFlagStore>(window, move |v, window, cx| {
let value = cx.flag_value::<T>();
if last_value.as_ref() == Some(&value) {
return;
}
last_value = Some(value.clone());
callback(value, v, window, cx);
})
}
fn when_flag_enabled<T: FeatureFlag>(
&mut self,
window: &mut Window,
callback: impl Fn(&mut V, &mut Window, &mut Context<V>) + Send + Sync + 'static,
) {
if self
.try_global::<FeatureFlagStore>()
.is_some_and(|f| f.has_flag::<T>(self))
{
self.defer_in(window, move |view, window, cx| {
callback(view, window, cx);
});
return;
}
let subscription = Rc::new(RefCell::new(None));
let inner = self.observe_global_in::<FeatureFlagStore>(window, {
let subscription = subscription.clone();
move |v, window, cx| {
let has_flag = cx.global::<FeatureFlagStore>().has_flag::<T>(cx);
if has_flag {
callback(v, window, cx);
subscription.take();
}
}
});
subscription.borrow_mut().replace(inner);
}
}
#[derive(Debug)]
pub struct OnFlagsReady {
pub is_staff: bool,
}
pub trait FeatureFlagAppExt {
fn update_flags(&mut self, staff: bool, flags: Vec<String>);
fn set_staff(&mut self, staff: bool);
fn has_flag<T: FeatureFlag>(&self) -> bool;
fn flag_value<T: FeatureFlag>(&self) -> T::Value;
fn is_staff(&self) -> bool;
fn on_flags_ready<F>(&mut self, callback: F) -> Subscription
where
F: FnMut(OnFlagsReady, &mut App) + 'static;
fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
where
F: FnMut(T::Value, &mut App) + 'static;
}
impl FeatureFlagAppExt for App {
fn update_flags(&mut self, staff: bool, flags: Vec<String>) {
let store = self.default_global::<FeatureFlagStore>();
store.update_server_flags(staff, flags);
}
fn set_staff(&mut self, staff: bool) {
let store = self.default_global::<FeatureFlagStore>();
store.set_staff(staff);
}
fn has_flag<T: FeatureFlag>(&self) -> bool {
self.try_global::<FeatureFlagStore>()
.map(|store| store.has_flag::<T>(self))
.unwrap_or_else(|| FeatureFlagStore::has_flag_default::<T>())
}
fn flag_value<T: FeatureFlag>(&self) -> T::Value {
self.try_global::<FeatureFlagStore>()
.and_then(|store| store.try_flag_value::<T>(self))
.unwrap_or_default()
}
fn is_staff(&self) -> bool {
self.try_global::<FeatureFlagStore>()
.map(|store| store.is_staff())
.unwrap_or(false)
}
fn on_flags_ready<F>(&mut self, mut callback: F) -> Subscription
where
F: FnMut(OnFlagsReady, &mut App) + 'static,
{
self.observe_global::<FeatureFlagStore>(move |cx| {
let store = cx.global::<FeatureFlagStore>();
if store.server_flags_received() {
callback(
OnFlagsReady {
is_staff: store.is_staff(),
},
cx,
);
}
})
}
fn observe_flag<T: FeatureFlag, F>(&mut self, mut callback: F) -> Subscription
where
F: FnMut(T::Value, &mut App) + 'static,
{
let mut last_value: Option<T::Value> = None;
self.observe_global::<FeatureFlagStore>(move |cx| {
let value = cx.flag_value::<T>();
if last_value.as_ref() == Some(&value) {
return;
}
last_value = Some(value.clone());
callback(value, cx);
})
}
}

View File

@@ -0,0 +1,145 @@
use crate::{EnumFeatureFlag, FeatureFlag, PresenceFlag, register_feature_flag};
pub struct NotebookFeatureFlag;
impl FeatureFlag for NotebookFeatureFlag {
const NAME: &'static str = "notebooks";
type Value = PresenceFlag;
}
register_feature_flag!(NotebookFeatureFlag);
pub struct PanicFeatureFlag;
impl FeatureFlag for PanicFeatureFlag {
const NAME: &'static str = "panic";
type Value = PresenceFlag;
}
register_feature_flag!(PanicFeatureFlag);
/// A feature flag for granting access to beta ACP features.
///
/// We reuse this feature flag for new betas, so don't delete it if it is not currently in use.
pub struct AcpBetaFeatureFlag;
impl FeatureFlag for AcpBetaFeatureFlag {
const NAME: &'static str = "acp-beta";
type Value = PresenceFlag;
}
register_feature_flag!(AcpBetaFeatureFlag);
pub struct AgentSharingFeatureFlag;
impl FeatureFlag for AgentSharingFeatureFlag {
const NAME: &'static str = "agent-sharing";
type Value = PresenceFlag;
}
register_feature_flag!(AgentSharingFeatureFlag);
pub struct AgentPanelTerminalFeatureFlag;
impl FeatureFlag for AgentPanelTerminalFeatureFlag {
const NAME: &'static str = "agent-panel-terminal";
type Value = PresenceFlag;
}
register_feature_flag!(AgentPanelTerminalFeatureFlag);
pub struct DiffReviewFeatureFlag;
impl FeatureFlag for DiffReviewFeatureFlag {
const NAME: &'static str = "diff-review";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
false
}
}
register_feature_flag!(DiffReviewFeatureFlag);
pub struct UpdatePlanToolFeatureFlag;
impl FeatureFlag for UpdatePlanToolFeatureFlag {
const NAME: &'static str = "update-plan-tool";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
false
}
}
register_feature_flag!(UpdatePlanToolFeatureFlag);
pub struct LspToolFeatureFlag;
impl FeatureFlag for LspToolFeatureFlag {
const NAME: &'static str = "lsp-tool";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
false
}
}
register_feature_flag!(LspToolFeatureFlag);
pub struct RenameToolFeatureFlag;
impl FeatureFlag for RenameToolFeatureFlag {
const NAME: &'static str = "rename-tool";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
true
}
}
register_feature_flag!(RenameToolFeatureFlag);
pub struct ProjectPanelUndoRedoFeatureFlag;
impl FeatureFlag for ProjectPanelUndoRedoFeatureFlag {
const NAME: &'static str = "project-panel-undo-redo";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
true
}
}
register_feature_flag!(ProjectPanelUndoRedoFeatureFlag);
/// Controls how agent thread worktree chips are labeled in the sidebar.
#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumFeatureFlag)]
pub enum AgentThreadWorktreeLabel {
#[default]
Both,
Worktree,
Branch,
}
pub struct AgentThreadWorktreeLabelFlag;
impl FeatureFlag for AgentThreadWorktreeLabelFlag {
const NAME: &'static str = "agent-thread-worktree-label";
type Value = AgentThreadWorktreeLabel;
fn enabled_for_staff() -> bool {
false
}
}
register_feature_flag!(AgentThreadWorktreeLabelFlag);
pub struct AutoWatchFeatureFlag;
impl FeatureFlag for AutoWatchFeatureFlag {
const NAME: &'static str = "auto-watch-screens";
type Value = PresenceFlag;
}
register_feature_flag!(AutoWatchFeatureFlag);
pub struct SkillsFeatureFlag;
impl FeatureFlag for SkillsFeatureFlag {
const NAME: &'static str = "skills";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
true
}
}
register_feature_flag!(SkillsFeatureFlag);

View File

@@ -0,0 +1,76 @@
use collections::HashMap;
use schemars::{Schema, json_schema};
use serde_json::{Map, Value};
use settings::{RegisterSetting, Settings, SettingsContent};
use crate::FeatureFlagStore;
#[derive(Clone, Debug, Default, RegisterSetting)]
pub struct FeatureFlagsSettings {
pub overrides: HashMap<String, String>,
}
impl Settings for FeatureFlagsSettings {
fn from_settings(content: &SettingsContent) -> Self {
Self {
overrides: content
.feature_flags
.as_ref()
.map(|map| map.0.clone())
.unwrap_or_default(),
}
}
}
/// Produces a JSON schema for the `feature_flags` object that lists each known
/// flag as a property with its variant keys as an `enum`.
///
/// Unknown flags are permitted via `additionalProperties: { "type": "string" }`,
/// so removing a flag from the binary never turns existing entries in
/// `settings.json` into validation errors.
pub fn generate_feature_flags_schema() -> Schema {
let mut properties = Map::new();
for descriptor in FeatureFlagStore::known_flags() {
let variants = (descriptor.variants)();
let enum_values: Vec<Value> = variants
.iter()
.map(|v| Value::String(v.override_key.to_string()))
.collect();
let enum_descriptions: Vec<Value> = variants
.iter()
.map(|v| Value::String(v.label.to_string()))
.collect();
let mut property = Map::new();
property.insert("type".to_string(), Value::String("string".to_string()));
property.insert("enum".to_string(), Value::Array(enum_values));
// VS Code / json-language-server use `enumDescriptions` for hover docs
// on each enum value; schemars passes them through untouched.
property.insert(
"enumDescriptions".to_string(),
Value::Array(enum_descriptions),
);
property.insert(
"description".to_string(),
Value::String(format!(
"Override for the `{}` feature flag. Default: `{}` (the {} variant).",
descriptor.name,
(descriptor.default_variant_key)(),
(descriptor.default_variant_key)(),
)),
);
properties.insert(descriptor.name.to_string(), Value::Object(property));
}
json_schema!({
"type": "object",
"description": "Local overrides for feature flags, keyed by flag name.",
"properties": properties,
"additionalProperties": {
"type": "string",
"description": "Unknown feature flag; retained so removed flags don't trip settings validation."
}
})
}

View File

@@ -0,0 +1,408 @@
use std::any::TypeId;
use std::sync::Arc;
use collections::HashMap;
use fs::Fs;
use gpui::{App, BorrowAppContext, Subscription};
use settings::{Settings, SettingsStore, update_settings_file};
use crate::{FeatureFlag, FeatureFlagValue, FeatureFlagsSettings, ZED_DISABLE_STAFF};
pub struct FeatureFlagDescriptor {
pub name: &'static str,
pub variants: fn() -> Vec<FeatureFlagVariant>,
pub on_variant_key: fn() -> &'static str,
pub default_variant_key: fn() -> &'static str,
pub enabled_for_all: fn() -> bool,
pub enabled_for_staff: fn() -> bool,
pub type_id: fn() -> TypeId,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FeatureFlagVariant {
pub override_key: &'static str,
pub label: &'static str,
}
inventory::collect!(FeatureFlagDescriptor);
#[doc(hidden)]
pub mod __private {
pub use inventory;
}
/// Submits a [`FeatureFlagDescriptor`] for this flag so it shows up in the
/// configuration UI and in `FeatureFlagStore::known_flags()`.
#[macro_export]
macro_rules! register_feature_flag {
($flag:ty) => {
$crate::__private::inventory::submit! {
$crate::FeatureFlagDescriptor {
name: <$flag as $crate::FeatureFlag>::NAME,
variants: || {
<<$flag as $crate::FeatureFlag>::Value as $crate::FeatureFlagValue>::all_variants()
.iter()
.map(|v| $crate::FeatureFlagVariant {
override_key: <<$flag as $crate::FeatureFlag>::Value as $crate::FeatureFlagValue>::override_key(v),
label: <<$flag as $crate::FeatureFlag>::Value as $crate::FeatureFlagValue>::label(v),
})
.collect()
},
on_variant_key: || {
<<$flag as $crate::FeatureFlag>::Value as $crate::FeatureFlagValue>::override_key(
&<<$flag as $crate::FeatureFlag>::Value as $crate::FeatureFlagValue>::on_variant(),
)
},
default_variant_key: || {
<<$flag as $crate::FeatureFlag>::Value as $crate::FeatureFlagValue>::override_key(
&<<$flag as $crate::FeatureFlag>::Value as ::std::default::Default>::default(),
)
},
enabled_for_all: <$flag as $crate::FeatureFlag>::enabled_for_all,
enabled_for_staff: <$flag as $crate::FeatureFlag>::enabled_for_staff,
type_id: || std::any::TypeId::of::<$flag>(),
}
}
};
}
#[derive(Default)]
pub struct FeatureFlagStore {
staff: bool,
server_flags: HashMap<String, String>,
server_flags_received: bool,
_settings_subscription: Option<Subscription>,
}
impl FeatureFlagStore {
pub fn init(cx: &mut App) {
let subscription = cx.observe_global::<SettingsStore>(|cx| {
// Touch the global so anything observing `FeatureFlagStore` re-runs
cx.update_default_global::<FeatureFlagStore, _>(|_, _| {});
});
cx.update_default_global::<FeatureFlagStore, _>(|store, _| {
store._settings_subscription = Some(subscription);
});
}
pub fn known_flags() -> impl Iterator<Item = &'static FeatureFlagDescriptor> {
let mut seen = collections::HashSet::default();
inventory::iter::<FeatureFlagDescriptor>().filter(move |d| seen.insert((d.type_id)()))
}
pub fn is_staff(&self) -> bool {
self.staff
}
pub fn server_flags_received(&self) -> bool {
self.server_flags_received
}
pub fn set_staff(&mut self, staff: bool) {
self.staff = staff;
}
pub fn update_server_flags(&mut self, staff: bool, flags: Vec<String>) {
self.staff = staff;
self.server_flags_received = true;
self.server_flags.clear();
for flag in flags {
self.server_flags.insert(flag.clone(), flag);
}
}
/// The user's override key for this flag, read directly from
/// [`FeatureFlagsSettings`].
pub fn override_for<'a>(flag_name: &str, cx: &'a App) -> Option<&'a str> {
FeatureFlagsSettings::get_global(cx)
.overrides
.get(flag_name)
.map(String::as_str)
}
/// Applies an override by writing to `settings.json`. The store's own
/// `overrides` field will be updated when the settings-store observer
/// fires. Pass the [`FeatureFlagValue::override_key`] of the variant
/// you want forced.
pub fn set_override(flag_name: &str, override_key: String, fs: Arc<dyn Fs>, cx: &App) {
let flag_name = flag_name.to_owned();
update_settings_file(fs, cx, move |content, _| {
content
.feature_flags
.get_or_insert_default()
.insert(flag_name, override_key);
});
}
/// Removes any override for the given flag from `settings.json`. Leaves
/// an empty `"feature_flags"` object rather than removing the key
/// entirely so the user can see it's still a meaningful settings surface.
pub fn clear_override(flag_name: &str, fs: Arc<dyn Fs>, cx: &App) {
let flag_name = flag_name.to_owned();
update_settings_file(fs, cx, move |content, _| {
if let Some(map) = content.feature_flags.as_mut() {
map.remove(&flag_name);
}
});
}
/// The resolved value of the flag for the current user, taking overrides,
/// `enabled_for_all`, staff rules, and server flags into account in that
/// order of precedence. Overrides are read directly from
/// [`FeatureFlagsSettings`].
pub fn try_flag_value<T: FeatureFlag>(&self, cx: &App) -> Option<T::Value> {
// `enabled_for_all` always wins, including over user overrides.
if T::enabled_for_all() {
return Some(T::Value::on_variant());
}
if let Some(override_key) = FeatureFlagsSettings::get_global(cx).overrides.get(T::NAME) {
return variant_from_key::<T::Value>(override_key);
}
// Staff default: resolve to the enabled variant.
if (cfg!(debug_assertions) || self.staff) && !*ZED_DISABLE_STAFF && T::enabled_for_staff() {
return Some(T::Value::on_variant());
}
// Server-delivered flag.
if let Some(wire) = self.server_flags.get(T::NAME) {
return T::Value::from_wire(wire);
}
None
}
/// Whether the flag resolves to its "on" value. Best for presence-style
/// flags. For enum flags with meaningful non-default variants, prefer
/// [`crate::FeatureFlagAppExt::flag_value`].
pub fn has_flag<T: FeatureFlag>(&self, cx: &App) -> bool {
self.try_flag_value::<T>(cx)
.is_some_and(|v| v == T::Value::on_variant())
}
/// Mirrors the resolution order of [`Self::try_flag_value`], but falls
/// back to the [`Default`] variant when no rule applies so the UI always
/// shows *something* selected — matching what
/// [`crate::FeatureFlagAppExt::flag_value`] would return.
pub fn resolved_key(&self, descriptor: &FeatureFlagDescriptor, cx: &App) -> &'static str {
let on_variant_key = (descriptor.on_variant_key)();
if (descriptor.enabled_for_all)() {
return on_variant_key;
}
if let Some(requested) = FeatureFlagsSettings::get_global(cx)
.overrides
.get(descriptor.name)
{
if let Some(variant) = (descriptor.variants)()
.into_iter()
.find(|v| v.override_key == requested.as_str())
{
return variant.override_key;
}
}
if (cfg!(debug_assertions) || self.staff)
&& !*ZED_DISABLE_STAFF
&& (descriptor.enabled_for_staff)()
{
return on_variant_key;
}
if self.server_flags.contains_key(descriptor.name) {
return on_variant_key;
}
(descriptor.default_variant_key)()
}
/// Whether this flag is forced on by `enabled_for_all` and therefore not
/// user-overridable. The UI uses this to render the row as disabled.
pub fn is_forced_on(descriptor: &FeatureFlagDescriptor) -> bool {
(descriptor.enabled_for_all)()
}
/// Fallback used when the store isn't installed as a global yet (e.g. very
/// early in startup). Matches the pre-existing default behavior.
pub fn has_flag_default<T: FeatureFlag>() -> bool {
if T::enabled_for_all() {
return true;
}
cfg!(debug_assertions) && T::enabled_for_staff() && !*ZED_DISABLE_STAFF
}
}
fn variant_from_key<V: FeatureFlagValue>(key: &str) -> Option<V> {
V::all_variants()
.iter()
.find(|v| v.override_key() == key)
.cloned()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{EnumFeatureFlag, FeatureFlag, PresenceFlag};
use gpui::UpdateGlobal;
use settings::SettingsStore;
struct DemoFlag;
impl FeatureFlag for DemoFlag {
const NAME: &'static str = "demo";
type Value = PresenceFlag;
fn enabled_for_staff() -> bool {
false
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, EnumFeatureFlag)]
enum Intensity {
#[default]
Low,
High,
}
struct IntensityFlag;
impl FeatureFlag for IntensityFlag {
const NAME: &'static str = "intensity";
type Value = Intensity;
fn enabled_for_all() -> bool {
true
}
}
fn init_settings_store(cx: &mut App) {
let store = SettingsStore::test(cx);
cx.set_global(store);
SettingsStore::update_global(cx, |store, _| {
store.register_setting::<FeatureFlagsSettings>();
});
}
fn set_override(name: &str, value: &str, cx: &mut App) {
SettingsStore::update_global(cx, |store: &mut SettingsStore, cx| {
store.update_user_settings(cx, |content| {
content
.feature_flags
.get_or_insert_default()
.insert(name.to_string(), value.to_string());
});
});
}
#[gpui::test]
fn server_flag_enables_presence(cx: &mut App) {
init_settings_store(cx);
let mut store = FeatureFlagStore::default();
assert!(!store.has_flag::<DemoFlag>(cx));
store.update_server_flags(false, vec!["demo".to_string()]);
assert!(store.has_flag::<DemoFlag>(cx));
}
#[gpui::test]
fn off_override_beats_server_flag(cx: &mut App) {
init_settings_store(cx);
let mut store = FeatureFlagStore::default();
store.update_server_flags(false, vec!["demo".to_string()]);
set_override(DemoFlag::NAME, "off", cx);
assert!(!store.has_flag::<DemoFlag>(cx));
assert_eq!(
store.try_flag_value::<DemoFlag>(cx),
Some(PresenceFlag::Off)
);
}
#[gpui::test]
fn enabled_for_all_wins_over_override(cx: &mut App) {
init_settings_store(cx);
let store = FeatureFlagStore::default();
set_override(IntensityFlag::NAME, "high", cx);
assert_eq!(
store.try_flag_value::<IntensityFlag>(cx),
Some(Intensity::Low)
);
}
#[gpui::test]
fn enum_override_selects_specific_variant(cx: &mut App) {
init_settings_store(cx);
let store = FeatureFlagStore::default();
// Staff path would normally resolve to `Low`; the override pushes
// us to `High` instead.
set_override("enum-demo", "high", cx);
struct EnumDemo;
impl FeatureFlag for EnumDemo {
const NAME: &'static str = "enum-demo";
type Value = Intensity;
}
assert_eq!(store.try_flag_value::<EnumDemo>(cx), Some(Intensity::High));
}
#[gpui::test]
fn unknown_variant_key_resolves_to_none(cx: &mut App) {
init_settings_store(cx);
let store = FeatureFlagStore::default();
set_override("enum-demo", "nonsense", cx);
struct EnumDemo;
impl FeatureFlag for EnumDemo {
const NAME: &'static str = "enum-demo";
type Value = Intensity;
}
assert_eq!(store.try_flag_value::<EnumDemo>(cx), None);
}
#[gpui::test]
fn on_override_enables_without_server_or_staff(cx: &mut App) {
init_settings_store(cx);
let store = FeatureFlagStore::default();
set_override(DemoFlag::NAME, "on", cx);
assert!(store.has_flag::<DemoFlag>(cx));
}
/// No rule applies, so the store's `try_flag_value` returns `None`. The
/// `FeatureFlagAppExt::flag_value` path (used by most callers) falls
/// back to [`Default`], which for `PresenceFlag` is `Off`.
#[gpui::test]
fn presence_flag_defaults_to_off(cx: &mut App) {
init_settings_store(cx);
let store = FeatureFlagStore::default();
assert_eq!(store.try_flag_value::<DemoFlag>(cx), None);
assert_eq!(PresenceFlag::default(), PresenceFlag::Off);
}
#[gpui::test]
fn on_flags_ready_waits_for_server_flags(cx: &mut gpui::TestAppContext) {
use crate::FeatureFlagAppExt;
use std::cell::Cell;
use std::rc::Rc;
cx.update(|cx| {
init_settings_store(cx);
FeatureFlagStore::init(cx);
});
let fired = Rc::new(Cell::new(false));
cx.update({
let fired = fired.clone();
|cx| cx.on_flags_ready(move |_, _| fired.set(true)).detach()
});
// Settings-triggered no-op touch must not fire on_flags_ready.
cx.update(|cx| cx.update_default_global::<FeatureFlagStore, _>(|_, _| {}));
cx.run_until_parked();
assert!(!fired.get());
// Server flags arrive — now it should fire.
cx.update(|cx| cx.update_flags(true, vec![]));
cx.run_until_parked();
assert!(fired.get());
}
}