feature_flags.rs

  1use gpui::{AppContext, Global, Subscription, ViewContext};
  2
  3#[derive(Default)]
  4struct FeatureFlags {
  5    flags: Vec<String>,
  6    staff: bool,
  7}
  8
  9impl FeatureFlags {
 10    fn has_flag(&self, flag: &str) -> bool {
 11        self.staff || self.flags.iter().any(|f| f.as_str() == flag)
 12    }
 13}
 14
 15impl Global for FeatureFlags {}
 16
 17/// To create a feature flag, implement this trait on a trivial type and use it as
 18/// a generic parameter when called [`FeatureFlagAppExt::has_flag`].
 19///
 20/// Feature flags are always enabled for members of Zed staff. To disable this behavior
 21/// so you can test flags being disabled, set ZED_DISABLE_STAFF=1 in your environment,
 22/// which will force Zed to treat the current user as non-staff.
 23pub trait FeatureFlag {
 24    const NAME: &'static str;
 25}
 26
 27pub struct Remoting {}
 28impl FeatureFlag for Remoting {
 29    const NAME: &'static str = "remoting";
 30}
 31
 32pub struct LanguageModels {}
 33impl FeatureFlag for LanguageModels {
 34    const NAME: &'static str = "language-models";
 35}
 36
 37pub struct TerminalInlineAssist {}
 38impl FeatureFlag for TerminalInlineAssist {
 39    const NAME: &'static str = "terminal-inline-assist";
 40}
 41
 42pub struct GroupedDiagnostics {}
 43impl FeatureFlag for GroupedDiagnostics {
 44    const NAME: &'static str = "grouped-diagnostics";
 45}
 46
 47pub trait FeatureFlagViewExt<V: 'static> {
 48    fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
 49    where
 50        F: Fn(bool, &mut V, &mut ViewContext<V>) + Send + Sync + 'static;
 51}
 52
 53impl<V> FeatureFlagViewExt<V> for ViewContext<'_, V>
 54where
 55    V: 'static,
 56{
 57    fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
 58    where
 59        F: Fn(bool, &mut V, &mut ViewContext<V>) + 'static,
 60    {
 61        self.observe_global::<FeatureFlags>(move |v, cx| {
 62            let feature_flags = cx.global::<FeatureFlags>();
 63            callback(feature_flags.has_flag(<T as FeatureFlag>::NAME), v, cx);
 64        })
 65    }
 66}
 67
 68pub trait FeatureFlagAppExt {
 69    fn update_flags(&mut self, staff: bool, flags: Vec<String>);
 70    fn set_staff(&mut self, staff: bool);
 71    fn has_flag<T: FeatureFlag>(&self) -> bool;
 72    fn is_staff(&self) -> bool;
 73
 74    fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
 75    where
 76        F: Fn(bool, &mut AppContext) + 'static;
 77}
 78
 79impl FeatureFlagAppExt for AppContext {
 80    fn update_flags(&mut self, staff: bool, flags: Vec<String>) {
 81        let feature_flags = self.default_global::<FeatureFlags>();
 82        feature_flags.staff = staff;
 83        feature_flags.flags = flags;
 84    }
 85
 86    fn set_staff(&mut self, staff: bool) {
 87        let feature_flags = self.default_global::<FeatureFlags>();
 88        feature_flags.staff = staff;
 89    }
 90
 91    fn has_flag<T: FeatureFlag>(&self) -> bool {
 92        self.try_global::<FeatureFlags>()
 93            .map(|flags| flags.has_flag(T::NAME))
 94            .unwrap_or(false)
 95    }
 96
 97    fn is_staff(&self) -> bool {
 98        self.try_global::<FeatureFlags>()
 99            .map(|flags| flags.staff)
100            .unwrap_or(false)
101    }
102
103    fn observe_flag<T: FeatureFlag, F>(&mut self, callback: F) -> Subscription
104    where
105        F: Fn(bool, &mut AppContext) + 'static,
106    {
107        self.observe_global::<FeatureFlags>(move |cx| {
108            let feature_flags = cx.global::<FeatureFlags>();
109            callback(feature_flags.has_flag(<T as FeatureFlag>::NAME), cx);
110        })
111    }
112}