1use std::{cell::RefCell, collections::HashMap};
2
3thread_local! {
4 pub static FEATURES: RefCell<FeatureFlags> = RefCell::new(FeatureFlags::new());
5}
6
7pub struct FeatureFlags {
10 features: HashMap<String, Feature>,
11}
12
13impl Default for FeatureFlags {
14 fn default() -> Self {
15 Self::new()
16 }
17}
18
19impl FeatureFlags {
20 pub fn new() -> FeatureFlags {
21 let mut features = HashMap::new();
22 if let Ok(val) = std::env::var("FEATURES") {
23 for feature_val in val.split(',') {
24 if let Some((key, f)) = parse_feature(feature_val) {
25 info!("adding feature flag ({}, {:?})", key, f);
26 features.insert(key, f);
27 }
28 }
29 }
30
31 FeatureFlags { features }
32 }
33
34 pub fn get(&self, key: &str) -> Option<&Feature> {
35 self.features.get(key)
36 }
37}
38
39#[derive(Clone, Debug, PartialEq, Eq)]
40pub enum Feature {
41 Boolean(bool),
42 String(String),
43 Number(i64),
44}
45
46impl Feature {
47 pub fn is_true(&self) -> bool {
48 self == &Feature::Boolean(true)
49 }
50
51 pub fn is_string(&self, s: &str) -> bool {
52 if let Feature::String(ref s1) = self {
53 s1 == s
54 } else {
55 false
56 }
57 }
58}
59
60fn parse_feature(s: &str) -> Option<(String, Feature)> {
61 let v = s.split(';').collect::<Vec<_>>();
62 if v.len() != 3 {
63 return None;
64 }
65
66 let f = if v[1] == "b" {
67 if let Ok(b) = v[2].parse() {
68 Feature::Boolean(b)
69 } else {
70 return None;
71 }
72 } else if v[1] == "s" {
73 Feature::String(v[2].to_string())
74 } else if v[1] == "i" {
75 if let Ok(i) = v[2].parse() {
76 Feature::Number(i)
77 } else {
78 return None;
79 }
80 } else {
81 return None;
82 };
83
84 Some((v[0].to_string(), f))
85}