statsig_rust/specs_adapter/
specs_adapter_trait.rs1use crate::statsig_err::StatsigErr;
2use crate::StatsigRuntime;
3use async_trait::async_trait;
4use serde::Deserialize;
5use serde::Serialize;
6use std::fmt::{self, Debug};
7use std::sync::Arc;
8use std::time::Duration;
9
10#[repr(u8)]
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub enum SpecsSource {
13 Uninitialized = 0,
14 NoValues,
15 Error,
16 Loading,
17 Bootstrap,
18 Adapter(String),
19 Network,
20}
21
22const DEFAULT_CONFIG_COMPRESSION_MODE: ConfigCompressionMode = ConfigCompressionMode::Gzip;
23
24#[derive(Clone)]
25pub enum ConfigCompressionMode {
26 Gzip,
27 Dictionary,
28}
29
30impl From<&str> for ConfigCompressionMode {
31 fn from(s: &str) -> Self {
32 match s.to_lowercase().as_str() {
33 "gzip" => ConfigCompressionMode::Gzip,
34 "dictionary" => ConfigCompressionMode::Dictionary,
35 _ => DEFAULT_CONFIG_COMPRESSION_MODE,
36 }
37 }
38}
39
40impl SpecsSource {
41 pub fn new_from_string(s: &str) -> Self {
42 if s.starts_with("Adapter(") {
43 let name = s
44 .strip_prefix("Adapter(")
45 .and_then(|s| s.strip_suffix(")"))
46 .unwrap_or("");
47 return SpecsSource::Adapter(name.to_string());
48 }
49
50 match s {
51 "Uninitialized" => SpecsSource::Uninitialized,
52 "NoValues" => SpecsSource::NoValues,
53 "Error" => SpecsSource::Error,
54 "Loading" => SpecsSource::Loading,
55 "Bootstrap" => SpecsSource::Bootstrap,
56 "Network" => SpecsSource::Network,
57 _ => SpecsSource::Error,
58 }
59 }
60}
61
62impl fmt::Display for SpecsSource {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 let s = match self {
65 SpecsSource::Adapter(name) => {
66 let type_name = format!("Adapter({name})");
67 type_name
68 }
69 SpecsSource::Uninitialized => "Uninitialized".to_string(),
70 SpecsSource::NoValues => "NoValues".to_string(),
71 SpecsSource::Error => "Error".to_string(),
72 SpecsSource::Loading => "Loading".to_string(),
73 SpecsSource::Network => "Network".to_string(),
74 SpecsSource::Bootstrap => "Bootstrap".to_string(),
75 };
76 write!(f, "{s}")
77 }
78}
79
80#[async_trait]
81pub trait SpecsAdapter: Send + Sync {
82 fn initialize(&self, listener: Arc<dyn SpecsUpdateListener>);
83
84 async fn start(
85 self: Arc<Self>,
86 statsig_runtime: &Arc<StatsigRuntime>,
87 ) -> Result<(), StatsigErr>;
88
89 async fn shutdown(
90 &self,
91 timeout: Duration,
92 statsig_runtime: &Arc<StatsigRuntime>,
93 ) -> Result<(), StatsigErr>;
94
95 async fn schedule_background_sync(
96 self: Arc<Self>,
97 statsig_runtime: &Arc<StatsigRuntime>,
98 ) -> Result<(), StatsigErr>;
99
100 fn get_type_name(&self) -> String;
101}
102
103pub struct SpecsUpdate {
104 pub data: Vec<u8>,
105 pub source: SpecsSource,
106 pub received_at: u64,
107 pub source_api: Option<String>,
108}
109
110#[repr(C)]
111#[derive(Serialize, Deserialize, Debug, Clone)]
112pub struct SpecsInfo {
113 pub lcut: Option<u64>,
114 pub checksum: Option<String>,
115 pub source: SpecsSource,
116 pub source_api: Option<String>,
117}
118
119impl SpecsInfo {
120 #[must_use]
121 pub fn empty() -> Self {
122 Self {
123 lcut: None,
124 checksum: None,
125 source: SpecsSource::NoValues,
126 source_api: None,
127 }
128 }
129
130 #[must_use]
131 pub fn error() -> Self {
132 Self {
133 lcut: None,
134 checksum: None,
135 source: SpecsSource::Error,
136 source_api: None,
137 }
138 }
139}
140
141pub trait SpecsUpdateListener: Send + Sync {
142 fn did_receive_specs_update(&self, update: SpecsUpdate) -> Result<(), StatsigErr>;
143
144 fn get_current_specs_info(&self) -> SpecsInfo;
145}
146
147impl fmt::Debug for dyn SpecsAdapter {
148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149 write!(f, "{}", self.get_type_name())
150 }
151}