rw_deno_core/
feature_checker.rs1use std::collections::BTreeSet;
4use std::fmt::Debug;
5
6pub type ExitCb = Box<dyn Fn(&str, &str) + Send + Sync>;
7pub type WarnCb = Box<dyn Fn(&str, &str) + Send + Sync>;
8
9fn exit(_feature: &str, _api_name: &str) {
10 std::process::exit(70);
11}
12
13fn warn_legacy_flag(_feature: &str, _api_name: &str) {}
14
15pub struct FeatureChecker {
16 features: BTreeSet<&'static str>,
17 legacy_unstable: bool,
20 warn_on_legacy_unstable: bool,
21 exit_cb: ExitCb,
22 warn_cb: WarnCb,
23}
24
25impl Default for FeatureChecker {
26 fn default() -> Self {
27 Self {
28 features: Default::default(),
29 legacy_unstable: false,
30 warn_on_legacy_unstable: false,
31 exit_cb: Box::new(exit),
32 warn_cb: Box::new(warn_legacy_flag),
33 }
34 }
35}
36
37impl Debug for FeatureChecker {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("FeatureChecker")
40 .field("features", &self.features)
41 .field("legacy_unstable", &self.legacy_unstable)
42 .field("warn_on_legacy_unstable", &self.warn_on_legacy_unstable)
43 .finish()
44 }
45}
46
47impl FeatureChecker {
48 pub fn enable_feature(&mut self, feature: &'static str) {
49 let inserted = self.features.insert(feature);
50 assert!(
51 inserted,
52 "Trying to enabled a feature that is already enabled {}",
53 feature
54 );
55 }
56
57 pub fn set_exit_cb(&mut self, cb: ExitCb) {
58 self.exit_cb = cb;
59 }
60
61 pub fn set_warn_cb(&mut self, cb: WarnCb) {
62 self.warn_cb = cb;
63 }
64
65 #[inline(always)]
69 pub fn check(&self, feature: &str) -> bool {
70 self.features.contains(feature)
71 }
72
73 #[inline(always)]
74 pub fn check_or_exit(&self, feature: &str, api_name: &str) {
75 if !self.check(feature) {
76 (self.exit_cb)(feature, api_name);
77 }
78 }
79
80 #[inline(always)]
81 pub fn check_or_exit_with_legacy_fallback(
82 &self,
83 feature: &str,
84 api_name: &str,
85 ) {
86 if !self.features.contains(feature) {
87 if self.legacy_unstable {
88 if self.warn_on_legacy_unstable {
89 (self.warn_cb)(feature, api_name);
90 }
91 return;
92 }
93
94 (self.exit_cb)(feature, api_name);
95 }
96 }
97
98 pub fn enable_legacy_unstable(&mut self) {
100 self.legacy_unstable = true;
101 }
102
103 pub fn warn_on_legacy_unstable(&mut self) {
105 self.warn_on_legacy_unstable = true;
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use std::sync::atomic::AtomicUsize;
112 use std::sync::atomic::Ordering;
113
114 use super::*;
115
116 #[test]
117 fn test_feature_checker() {
118 static EXIT_COUNT: AtomicUsize = AtomicUsize::new(0);
119 static WARN_COUNT: AtomicUsize = AtomicUsize::new(0);
120
121 fn exit_cb(_feature: &str, _api_name: &str) {
122 EXIT_COUNT.fetch_add(1, Ordering::Relaxed);
123 }
124
125 fn warn_cb(_feature: &str, _api_name: &str) {
126 WARN_COUNT.fetch_add(1, Ordering::Relaxed);
127 }
128
129 let mut checker = FeatureChecker::default();
130 checker.set_exit_cb(Box::new(exit_cb));
131 checker.set_warn_cb(Box::new(warn_cb));
132 checker.enable_feature("foobar");
133
134 assert!(checker.check("foobar"));
135 assert!(!checker.check("fizzbuzz"));
136
137 checker.check_or_exit("foobar", "foo");
138 assert_eq!(EXIT_COUNT.load(Ordering::Relaxed), 0);
139 checker.check_or_exit("fizzbuzz", "foo");
140 assert_eq!(EXIT_COUNT.load(Ordering::Relaxed), 1);
141
142 checker.enable_legacy_unstable();
143 checker.check_or_exit_with_legacy_fallback("fizzbuzz", "foo");
144 assert_eq!(EXIT_COUNT.load(Ordering::Relaxed), 1);
145 assert_eq!(WARN_COUNT.load(Ordering::Relaxed), 0);
146
147 checker.warn_on_legacy_unstable();
148 checker.check_or_exit_with_legacy_fallback("fizzbuzz", "foo");
149 assert_eq!(EXIT_COUNT.load(Ordering::Relaxed), 1);
150 assert_eq!(WARN_COUNT.load(Ordering::Relaxed), 1);
151 }
152}