Skip to main content

statsig_rust/
interned_string.rs

1use 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    // String literal -> from_str_ref
56    ($value:literal) => {
57        InternedString::from_str_ref($value)
58    };
59    // Bool -> from_bool
60    (bool: $value:literal) => {
61        InternedString::from_bool($value)
62    };
63    // String (owned) -> from_string
64    ($value:expr) => {
65        InternedString::from_string($value)
66    };
67    // DynamicString -> from_dynamic_string
68    ($value:expr) => {
69        InternedString::from_dynamic_string($value)
70    };
71    // String parts (slice) -> from_str_parts
72    ( $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    /// Clones the value out of the Arc. This is not performant.
128    /// Please only use this if we are giving a value to a caller outside of this library.
129    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), // zero-copy for serde_json::from_str
156            Owned(String), // allocation for serde_json::from_value
157        }
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}