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