statsig_rust/evaluation/
dynamic_string.rs1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use serde_json::Value;
3
4use crate::{hashing::ahash_str, interned_string::InternedString};
5
6lazy_static::lazy_static! {
7 static ref USER_ID: DynamicString = DynamicString::from("userID".to_string());
8 static ref STABLE_ID: DynamicString = DynamicString::from("stableID".to_string());
9}
10
11#[derive(Clone, Eq, Debug)]
12pub struct DynamicString {
13 pub value: InternedString,
14 pub lowercased_value: InternedString,
15 pub hash_value: u64,
16}
17
18impl DynamicString {
19 pub fn user_id() -> Self {
20 USER_ID.clone()
21 }
22
23 pub fn stable_id() -> Self {
24 STABLE_ID.clone()
25 }
26
27 pub fn from_bool(value: bool) -> Self {
28 let raw = if value { "true" } else { "false" };
29 Self {
30 value: InternedString::from_bool(value),
31 lowercased_value: InternedString::from_bool(value),
32 hash_value: ahash_str(raw),
33 }
34 }
35}
36
37impl Serialize for DynamicString {
40 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
41 where
42 S: Serializer,
43 {
44 if let Ok(bool_value) = self.value.parse::<bool>() {
45 return serializer.serialize_bool(bool_value);
46 }
47
48 serializer.serialize_str(&self.value)
49 }
50}
51
52impl<'de> Deserialize<'de> for DynamicString {
53 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
54 where
55 D: Deserializer<'de>,
56 {
57 let value = String::deserialize(deserializer)?;
58 Ok(DynamicString::from(value))
59 }
60}
61
62impl PartialEq for DynamicString {
65 fn eq(&self, other: &Self) -> bool {
66 self.value == other.value
67 }
68}
69
70impl PartialEq<&str> for DynamicString {
71 fn eq(&self, other: &&str) -> bool {
72 self.value.as_str() == *other
73 }
74}
75
76impl PartialEq<String> for DynamicString {
77 fn eq(&self, other: &String) -> bool {
78 self.value == *other
79 }
80}
81
82impl PartialEq<&String> for DynamicString {
83 fn eq(&self, other: &&String) -> bool {
84 self.value.as_str() == other.as_str()
85 }
86}
87
88impl From<Value> for DynamicString {
91 fn from(value: Value) -> Self {
92 let str_value = match value.as_str() {
93 Some(value) => value.to_string(),
94 None => value.to_string(),
95 };
96 DynamicString::from(str_value)
97 }
98}
99
100impl From<String> for DynamicString {
101 fn from(value: String) -> Self {
102 let hash_value = ahash_str(&value);
103 let value = InternedString::from_string(value);
104 let lowercased_value = if value.as_str().bytes().any(|byte| byte.is_ascii_uppercase()) {
105 InternedString::from_string(value.as_str().to_lowercase())
106 } else {
107 value.clone()
108 };
109
110 Self {
111 value,
112 lowercased_value,
113 hash_value,
114 }
115 }
116}