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 Network,
18 Bootstrap,
19 Adapter(String),
20}
21
22impl SpecsSource {
23 pub fn new_from_string(s: &str) -> Self {
24 if s.starts_with("Adapter(") {
25 let name = s
26 .strip_prefix("Adapter(")
27 .and_then(|s| s.strip_suffix(")"))
28 .unwrap_or("");
29 return SpecsSource::Adapter(name.to_string());
30 }
31
32 match s {
33 "Uninitialized" => SpecsSource::Uninitialized,
34 "NoValues" => SpecsSource::NoValues,
35 "Error" => SpecsSource::Error,
36 "Loading" => SpecsSource::Loading,
37 "Network" => SpecsSource::Network,
38 "Bootstrap" => SpecsSource::Bootstrap,
39 _ => SpecsSource::Error,
40 }
41 }
42}
43
44impl fmt::Display for SpecsSource {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 let s = match self {
47 SpecsSource::Adapter(name) => {
48 let type_name = format!("Adapter({name})");
49 type_name
50 }
51 SpecsSource::Uninitialized => "Uninitialized".to_string(),
52 SpecsSource::NoValues => "NoValues".to_string(),
53 SpecsSource::Error => "Error".to_string(),
54 SpecsSource::Loading => "Loading".to_string(),
55 SpecsSource::Network => "Network".to_string(),
56 SpecsSource::Bootstrap => "Bootstrap".to_string(),
57 };
58 write!(f, "{s}")
59 }
60}
61
62#[async_trait]
63pub trait SpecsAdapter: Send + Sync {
64 fn initialize(&self, listener: Arc<dyn SpecsUpdateListener>);
65
66 async fn start(
67 self: Arc<Self>,
68 statsig_runtime: &Arc<StatsigRuntime>,
69 ) -> Result<(), StatsigErr>;
70
71 async fn shutdown(
72 &self,
73 timeout: Duration,
74 statsig_runtime: &Arc<StatsigRuntime>,
75 ) -> Result<(), StatsigErr>;
76
77 async fn schedule_background_sync(
78 self: Arc<Self>,
79 statsig_runtime: &Arc<StatsigRuntime>,
80 ) -> Result<(), StatsigErr>;
81
82 fn get_type_name(&self) -> String;
83}
84
85pub struct SpecsUpdate {
86 pub data: String,
87 pub source: SpecsSource,
88 pub received_at: u64,
89}
90
91#[repr(C)]
92#[derive(Serialize, Deserialize, Debug)]
93pub struct SpecsInfo {
94 pub lcut: Option<u64>,
95 pub checksum: Option<String>,
96 pub zstd_dict_id: Option<String>,
97 pub source: SpecsSource,
98}
99
100impl SpecsInfo {
101 #[must_use]
102 pub fn empty() -> Self {
103 Self {
104 lcut: None,
105 checksum: None,
106 zstd_dict_id: None,
107 source: SpecsSource::NoValues,
108 }
109 }
110
111 #[must_use]
112 pub fn error() -> Self {
113 Self {
114 lcut: None,
115 checksum: None,
116 zstd_dict_id: None,
117 source: SpecsSource::Error,
118 }
119 }
120}
121
122pub trait SpecsUpdateListener: Send + Sync {
123 fn did_receive_specs_update(&self, update: SpecsUpdate) -> Result<(), StatsigErr>;
124
125 fn get_current_specs_info(&self) -> SpecsInfo;
126}
127
128impl fmt::Debug for dyn SpecsAdapter {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 write!(f, "{}", self.get_type_name())
131 }
132}