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}