rama_proxy/proxydb/
str.rs1use serde::{Deserialize, Serialize};
2use std::{convert::Infallible, str::FromStr};
3use unicode_normalization::UnicodeNormalization;
4
5#[derive(Debug, Clone)]
6pub struct StringFilter(String);
14
15impl StringFilter {
16 pub fn any() -> Self {
18 "*".into()
19 }
20
21 pub fn new(value: impl AsRef<str>) -> Self {
23 Self(value.as_ref().trim().to_lowercase().nfc().collect())
24 }
25
26 pub fn inner(&self) -> &str {
28 &self.0
29 }
30
31 pub fn into_inner(self) -> String {
33 self.0
34 }
35
36 pub fn is_any(&self) -> bool {
38 self.0 == "*"
39 }
40}
41
42impl PartialEq for StringFilter {
43 fn eq(&self, other: &Self) -> bool {
44 match (self.0.as_str(), other.0.as_str()) {
45 ("*", _) | (_, "*") => true,
46 _ => self.0 == other.0,
47 }
48 }
49}
50
51impl Eq for StringFilter {}
52
53impl std::hash::Hash for StringFilter {
54 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
55 self.0.hash(state);
56 }
57}
58
59impl AsRef<str> for StringFilter {
60 fn as_ref(&self) -> &str {
61 &self.0
62 }
63}
64
65impl FromStr for StringFilter {
66 type Err = Infallible;
67
68 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 Ok(Self::new(s))
70 }
71}
72
73impl std::fmt::Display for StringFilter {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 write!(f, "{}", self.0)
76 }
77}
78
79impl From<StringFilter> for String {
80 fn from(filter: StringFilter) -> Self {
81 filter.0
82 }
83}
84
85impl From<&StringFilter> for String {
86 fn from(filter: &StringFilter) -> Self {
87 filter.0.clone()
88 }
89}
90
91impl From<&str> for StringFilter {
92 fn from(value: &str) -> Self {
93 Self::new(value)
94 }
95}
96
97impl From<String> for StringFilter {
98 fn from(value: String) -> Self {
99 Self::new(value)
100 }
101}
102
103impl From<&String> for StringFilter {
104 fn from(value: &String) -> Self {
105 Self::new(value)
106 }
107}
108
109impl Serialize for StringFilter {
110 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
111 where
112 S: serde::Serializer,
113 {
114 self.0.serialize(serializer)
115 }
116}
117
118impl<'de> Deserialize<'de> for StringFilter {
119 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120 where
121 D: serde::Deserializer<'de>,
122 {
123 String::deserialize(deserializer).map(Self::new)
124 }
125}
126
127#[cfg(feature = "memory-db")]
128impl venndb::Any for StringFilter {
129 fn is_any(&self) -> bool {
130 Self::is_any(self)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn test_string_filter_creation() {
140 let filter = StringFilter::new(" Hello World ");
141 assert_eq!(filter, "hello world".into());
142 }
143
144 #[test]
145 fn test_string_filter_nfc() {
146 let filter = StringFilter::new("ÅΩ");
147 assert_eq!(filter, "ÅΩ".into());
148 }
149
150 #[test]
151 fn test_string_filter_case_insensitive() {
152 let filter = StringFilter::new("Hello World");
153 assert_eq!(filter, "hello world".into());
154 }
155
156 #[test]
157 fn test_string_filter_deref() {
158 let filter = StringFilter::new("Hello World");
159 assert_eq!(filter.as_ref().to_ascii_uppercase(), "HELLO WORLD");
160 }
161
162 #[test]
163 fn test_string_filter_as_str() {
164 let filter = StringFilter::new("Hello World");
165 assert_eq!(filter.as_ref(), "hello world");
166 }
167
168 #[test]
169 fn test_string_filter_serialization() {
170 let filter = StringFilter::new("Hello World");
171 let json = serde_json::to_string(&filter).unwrap();
172 assert_eq!(json, "\"hello world\"");
173 let filter2: StringFilter = serde_json::from_str(&json).unwrap();
174 assert_eq!(filter, filter2);
175 }
176
177 #[test]
178 fn test_string_filter_deserialization() {
179 let json = "\" Hello World\"";
180 let filter: StringFilter = serde_json::from_str(json).unwrap();
181 assert_eq!(filter, "hello world".into());
182 }
183
184 #[test]
185 fn test_string_filter_any() {
186 let filter = StringFilter::any();
187 assert!(filter.is_any());
188
189 let filter: StringFilter = "hello".into();
190 assert!(!filter.is_any());
191 }
192
193 #[test]
194 fn test_string_filter_eq_cases() {
195 for (a, b) in [
196 ("hello", "hello"),
197 ("hello", "HELLO"),
198 ("HELLO", "hello"),
199 ("HELLO", "HELLO"),
200 (" foo", "foo "),
201 ("foo ", " foo"),
202 (" FOO ", " foo"),
203 ("*", "*"),
204 ("*", "foo"),
205 ("foo", "*"),
206 (" * ", "foo"),
207 ("foo", " * "),
208 ] {
209 let a: StringFilter = a.into();
210 let b: StringFilter = b.into();
211 assert_eq!(a, b);
212 }
213 }
214
215 #[test]
216 fn test_string_filter_neq() {
217 for (a, b) in [("hello", "world"), ("world", "hello")] {
218 let a: StringFilter = a.into();
219 let b: StringFilter = b.into();
220 assert_ne!(a, b);
221 }
222 }
223}