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, hashing, 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 Uninterned(Arc<String>),
34 Static(&'static str),
35}
36
37impl InternedStringValue {
38 pub fn as_str(&self) -> &str {
39 match self {
40 InternedStringValue::Pointer(value) => value.as_str(),
41 InternedStringValue::Uninterned(value) => value.as_str(),
42 InternedStringValue::Static(value) => value,
43 }
44 }
45}
46
47impl PartialEq for InternedStringValue {
48 fn eq(&self, other: &Self) -> bool {
49 self.as_str() == other.as_str()
50 }
51}
52
53#[macro_export]
54macro_rules! interned_str {
55 ($value:literal) => {
57 InternedString::from_str_ref($value)
58 };
59 (bool: $value:literal) => {
61 InternedString::from_bool($value)
62 };
63 ($value:expr) => {
65 InternedString::from_string($value)
66 };
67 ($value:expr) => {
69 InternedString::from_dynamic_string($value)
70 };
71 ( $value:expr) => {
73 InternedString::from_str_parts($value)
74 };
75}
76
77impl InternedString {
78 pub fn default_rule_id_ref() -> &'static Self {
79 &DEFAULT_RULE_ID
80 }
81
82 pub fn default_rule_id() -> Self {
83 DEFAULT_RULE_ID.clone()
84 }
85
86 pub fn from_str_ref(value: &str) -> Self {
87 InternedStore::get_or_intern_string(value)
88 }
89
90 pub fn from_string(value: String) -> Self {
91 InternedStore::get_or_intern_owned_string(value)
92 }
93
94 pub fn from_string_uninterned(value: String) -> Self {
95 let hash = hashing::hash_one(value.as_bytes());
96 Self {
97 hash,
98 value: InternedStringValue::Uninterned(Arc::new(value)),
99 }
100 }
101
102 pub fn from_bool(value: bool) -> Self {
103 if value {
104 TRUE_STRING.clone()
105 } else {
106 FALSE_STRING.clone()
107 }
108 }
109
110 pub fn from_dynamic_string(value: &DynamicString) -> Self {
111 InternedStore::get_or_intern_string(value.value.as_str())
112 }
113
114 pub fn from_str_parts(parts: &[&str]) -> Self {
115 let mut value = String::new();
116 for v in parts {
117 value.push_str(v);
118 }
119
120 Self::from_string_uninterned(value)
121 }
122
123 pub fn as_str(&self) -> &str {
124 self.value.as_str()
125 }
126
127 pub fn unperformant_to_string(&self) -> String {
130 self.value.as_str().to_string()
131 }
132
133 pub fn empty_ref() -> &'static Self {
134 &EMPTY
135 }
136
137 pub fn empty() -> Self {
138 EMPTY.clone()
139 }
140
141 pub fn is_empty(&self) -> bool {
142 self.value.as_str().is_empty()
143 }
144}
145
146impl<'de> Deserialize<'de> for InternedString {
147 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
148 where
149 D: Deserializer<'de>,
150 {
151 #[derive(Deserialize)]
152 #[serde(untagged)]
153 enum CowString<'a> {
154 #[serde(borrow)]
155 Borrowed(&'a str), Owned(String), }
158
159 let raw: CowString<'de> = CowString::deserialize(deserializer)?;
160 match raw {
161 CowString::Borrowed(raw) => Ok(InternedStore::get_or_intern_string(raw)),
162 CowString::Owned(raw) => Ok(InternedStore::get_or_intern_owned_string(raw)),
163 }
164 }
165}
166
167impl Serialize for InternedString {
168 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169 where
170 S: Serializer,
171 {
172 serializer.serialize_str(self.value.as_str())
173 }
174}
175
176impl PartialEq for InternedString {
177 fn eq(&self, other: &Self) -> bool {
178 self.as_str() == other.as_str()
179 }
180}
181
182impl Hash for InternedString {
183 fn hash<H: Hasher>(&self, state: &mut H) {
184 self.hash.hash(state);
185 }
186}
187
188impl PartialEq<&str> for InternedString {
189 fn eq(&self, other: &&str) -> bool {
190 self.as_str() == *other
191 }
192}
193
194impl PartialEq<str> for InternedString {
195 fn eq(&self, other: &str) -> bool {
196 self.as_str() == other
197 }
198}
199
200impl PartialEq<String> for InternedString {
201 fn eq(&self, other: &String) -> bool {
202 self.as_str() == *other
203 }
204}
205
206impl Deref for InternedString {
207 type Target = str;
208
209 fn deref(&self) -> &Self::Target {
210 self.as_str()
211 }
212}
213
214impl Display for InternedString {
215 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216 write!(f, "{}", self.value.as_str())
217 }
218}
219
220impl Default for InternedString {
221 fn default() -> Self {
222 EMPTY.clone()
223 }
224}