rw_deno_core/
feature_checker.rs

1// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
2
3use 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  // TODO(bartlomieju): remove once we migrate away from `--unstable` flag
18  // in the CLI.
19  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  /// Check if a feature is enabled.
66  ///
67  /// If a feature in not present in the checker, return false.
68  #[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  // TODO(bartlomieju): remove this.
99  pub fn enable_legacy_unstable(&mut self) {
100    self.legacy_unstable = true;
101  }
102
103  // TODO(bartlomieju): remove this.
104  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}