statsig_rust/evaluation/
dynamic_returnable.rs1use std::{borrow::Cow, collections::HashMap, sync::Arc};
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use serde_json::{
5 value::{to_raw_value, RawValue},
6 Value as JsonValue,
7};
8
9use crate::{interned_values::InternedStore, log_e};
10
11const TAG: &str = "DynamicReturnable";
12
13lazy_static::lazy_static! {
14 static ref EMPTY_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
15 hash: 0,
16 value: DynamicReturnableValue::Null,
17 };
18
19 static ref TRUE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
20 hash: 0,
21 value: DynamicReturnableValue::Bool(true),
22 };
23
24 static ref FALSE_DYNAMIC_RETURNABLE: DynamicReturnable = DynamicReturnable {
25 hash: 0,
26 value: DynamicReturnableValue::Bool(false),
27 };
28
29}
30
31#[derive(Clone, PartialEq, Debug)]
32pub struct DynamicReturnable {
33 pub hash: u64,
34 pub value: DynamicReturnableValue,
35}
36
37impl DynamicReturnable {
38 pub fn empty() -> Self {
39 EMPTY_DYNAMIC_RETURNABLE.clone()
40 }
41
42 pub fn from_bool(value: bool) -> Self {
43 if value {
44 TRUE_DYNAMIC_RETURNABLE.clone()
45 } else {
46 FALSE_DYNAMIC_RETURNABLE.clone()
47 }
48 }
49
50 pub fn from_map(value: HashMap<String, JsonValue>) -> Self {
51 let raw_value = match to_raw_value(&value) {
52 Ok(raw_value) => raw_value,
53 Err(e) => {
54 log_e!(TAG, "Failed to convert map to raw value: {}", e);
55 return Self::empty();
56 }
57 };
58
59 InternedStore::get_or_intern_returnable(Cow::Owned(raw_value))
60 }
61
62 pub fn get_bool(&self) -> Option<bool> {
63 match self.value {
64 DynamicReturnableValue::Bool(value) => Some(value),
65 _ => None,
66 }
67 }
68
69 pub fn get_json(&self) -> Option<HashMap<String, JsonValue>> {
70 let bytes = match &self.value {
71 DynamicReturnableValue::JsonPointer(bytes) => bytes.get().as_bytes(),
72 DynamicReturnableValue::JsonStatic(bytes) => bytes.get().as_bytes(),
73 _ => return None,
74 };
75
76 match serde_json::from_slice(bytes) {
77 Ok(json) => Some(json),
78 Err(e) => {
79 log_e!(TAG, "Failed to parse json. Error: {}", e);
80 None
81 }
82 }
83 }
84
85 pub fn get_hash(&self) -> u64 {
86 self.hash
87 }
88}
89
90impl<'de> Deserialize<'de> for DynamicReturnable {
91 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
92 where
93 D: Deserializer<'de>,
94 {
95 let raw_value_ref: Cow<RawValue> = Deserialize::deserialize(deserializer)?;
96 Ok(InternedStore::get_or_intern_returnable(raw_value_ref))
97 }
98}
99
100impl Serialize for DynamicReturnable {
101 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
102 where
103 S: Serializer,
104 {
105 match &self.value {
106 DynamicReturnableValue::JsonPointer(raw) => raw.serialize(serializer),
107 DynamicReturnableValue::JsonStatic(raw) => raw.serialize(serializer),
108 DynamicReturnableValue::Null => serializer.serialize_none(),
109 DynamicReturnableValue::Bool(value) => serializer.serialize_bool(*value),
110 }
111 }
112}
113
114impl Drop for DynamicReturnable {
115 fn drop(&mut self) {
116 self.value = DynamicReturnableValue::Null;
117 InternedStore::release_returnable(self.hash);
118 }
119}
120
121#[derive(Clone, Debug)]
124pub enum DynamicReturnableValue {
125 Null,
126 Bool(bool),
127 JsonPointer(Arc<Box<RawValue>>),
128 JsonStatic(&'static RawValue),
129}
130
131impl DynamicReturnableValue {
132 fn raw_string_value(&self) -> Option<&str> {
133 match self {
134 DynamicReturnableValue::JsonPointer(raw) => Some(raw.as_ref().get()),
135 DynamicReturnableValue::JsonStatic(raw) => Some(raw.get()),
136 _ => None,
137 }
138 }
139}
140
141impl PartialEq for DynamicReturnableValue {
142 fn eq(&self, other: &Self) -> bool {
143 match (self, other) {
144 (DynamicReturnableValue::Null, DynamicReturnableValue::Null) => true,
145 (DynamicReturnableValue::Bool(a), DynamicReturnableValue::Bool(b)) => *a == *b,
146 (left, right) => left.raw_string_value() == right.raw_string_value(),
147 }
148 }
149}