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