statsig_rust/
interned_string.rs1use std::{
2 fmt::Display,
3 hash::{Hash, Hasher},
4 ops::Deref,
5 sync::Arc,
6};
7
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10use crate::{evaluation::dynamic_string::DynamicString, interned_values::InternedStore};
11
12lazy_static::lazy_static! {
13 static ref EMPTY: InternedString = InternedString {
14 hash: 0,
15 value: InternedStringValue::Static(""),
16 };
17
18 static ref TRUE_STRING: InternedString = InternedString::from_string("true".to_string());
19 static ref FALSE_STRING: InternedString = InternedString::from_string("false".to_string());
20
21 static ref DEFAULT_RULE_ID: InternedString = InternedString::from_str_ref("default");
22}
23
24#[derive(Clone, Debug, Eq)]
25pub struct InternedString {
26 pub hash: u64,
27 pub value: InternedStringValue,
28}
29
30#[derive(Clone, Debug, Eq)]
31pub enum InternedStringValue {
32 Pointer(Arc<String>),
33 Static(&'static str),
34}
35
36impl InternedStringValue {
37 pub fn as_str(&self) -> &str {
38 match self {
39 InternedStringValue::Pointer(value) => value.as_str(),
40 InternedStringValue::Static(value) => value,
41 }
42 }
43}
44
45impl PartialEq for InternedStringValue {
46 fn eq(&self, other: &Self) -> bool {
47 self.as_str() == other.as_str()
48 }
49}
50
51#[macro_export]
52macro_rules! interned_str {
53 ($value:literal) => {
55 InternedString::from_str_ref($value)
56 };
57 (bool: $value:literal) => {
59 InternedString::from_bool($value)
60 };
61 ($value:expr) => {
63 InternedString::from_string($value)
64 };
65 ($value:expr) => {
67 InternedString::from_dynamic_string($value)
68 };
69 ( $value:expr) => {
71 InternedString::from_str_parts($value)
72 };
73}
74
75impl InternedString {
76 pub fn default_rule_id_ref() -> &'static Self {
77 &DEFAULT_RULE_ID
78 }
79
80 pub fn default_rule_id() -> Self {
81 DEFAULT_RULE_ID.clone()
82 }
83
84 pub fn from_str_ref(value: &str) -> Self {
85 InternedStore::get_or_intern_string(value)
86 }
87
88 pub fn from_string(value: String) -> Self {
89 InternedStore::get_or_intern_string(value)
90 }
91
92 pub fn from_bool(value: bool) -> Self {
93 if value {
94 TRUE_STRING.clone()
95 } else {
96 FALSE_STRING.clone()
97 }
98 }
99
100 pub fn from_dynamic_string(value: &DynamicString) -> Self {
101 InternedStore::get_or_intern_string(value.value.as_str())
102 }
103
104 pub fn from_str_parts(parts: &[&str]) -> Self {
105 let mut value = String::new();
106 for v in parts {
107 value.push_str(v);
108 }
109
110 Self::from_string(value)
111 }
112
113 pub fn as_str(&self) -> &str {
114 self.value.as_str()
115 }
116
117 pub fn unperformant_to_string(&self) -> String {
120 self.value.as_str().to_string()
121 }
122
123 pub fn empty_ref() -> &'static Self {
124 &EMPTY
125 }
126
127 pub fn empty() -> Self {
128 EMPTY.clone()
129 }
130
131 pub fn is_empty(&self) -> bool {
132 self.value.as_str().is_empty()
133 }
134}
135
136impl<'de> Deserialize<'de> for InternedString {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: Deserializer<'de>,
140 {
141 #[derive(Deserialize)]
142 #[serde(untagged)]
143 enum CowString<'a> {
144 #[serde(borrow)]
145 Borrowed(&'a str), Owned(String), }
148
149 let raw: CowString<'de> = CowString::deserialize(deserializer)?;
150 match raw {
151 CowString::Borrowed(raw) => Ok(InternedStore::get_or_intern_string(raw)),
152 CowString::Owned(raw) => Ok(InternedStore::get_or_intern_string(raw)),
153 }
154 }
155}
156
157impl Serialize for InternedString {
158 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159 where
160 S: Serializer,
161 {
162 serializer.serialize_str(self.value.as_str())
163 }
164}
165
166impl PartialEq for InternedString {
167 fn eq(&self, other: &Self) -> bool {
168 self.as_str() == other.as_str()
169 }
170}
171
172impl Hash for InternedString {
173 fn hash<H: Hasher>(&self, state: &mut H) {
174 self.hash.hash(state);
175 }
176}
177
178impl PartialEq<&str> for InternedString {
179 fn eq(&self, other: &&str) -> bool {
180 self.as_str() == *other
181 }
182}
183
184impl PartialEq<str> for InternedString {
185 fn eq(&self, other: &str) -> bool {
186 self.as_str() == other
187 }
188}
189
190impl PartialEq<String> for InternedString {
191 fn eq(&self, other: &String) -> bool {
192 self.as_str() == *other
193 }
194}
195
196impl Deref for InternedString {
197 type Target = str;
198
199 fn deref(&self) -> &Self::Target {
200 self.as_str()
201 }
202}
203
204impl Display for InternedString {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 write!(f, "{}", self.value.as_str())
207 }
208}
209
210impl Default for InternedString {
211 fn default() -> Self {
212 EMPTY.clone()
213 }
214}
215
216impl Drop for InternedString {
217 fn drop(&mut self) {
218 self.value = InternedStringValue::Static("");
219 InternedStore::release_string(self.hash);
220 }
221}