cipherstash_config/column/index/
array_index_mode.rs1use bitflags::bitflags;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4bitflags! {
5 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
10 pub struct ArrayIndexMode: u8 {
11 const NONE = 0b0000;
13 const ITEM = 0b0001;
15 const WILDCARD = 0b0010;
17 const POSITION = 0b0100;
19 const ALL = Self::ITEM.bits() | Self::WILDCARD.bits() | Self::POSITION.bits();
21 }
22}
23
24impl ArrayIndexMode {
25 pub fn has_item(&self) -> bool {
27 self.contains(Self::ITEM)
28 }
29
30 pub fn has_wildcard(&self) -> bool {
32 self.contains(Self::WILDCARD)
33 }
34
35 pub fn has_position(&self) -> bool {
37 self.contains(Self::POSITION)
38 }
39}
40
41#[derive(Serialize, Deserialize)]
43struct ArrayIndexModeObject {
44 #[serde(default)]
45 item: bool,
46 #[serde(default)]
47 wildcard: bool,
48 #[serde(default)]
49 position: bool,
50}
51
52impl Serialize for ArrayIndexMode {
53 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
54 where
55 S: Serializer,
56 {
57 if *self == Self::ALL {
59 serializer.serialize_str("all")
60 } else if *self == Self::NONE {
61 serializer.serialize_str("none")
62 } else {
63 let obj = ArrayIndexModeObject {
64 item: self.has_item(),
65 wildcard: self.has_wildcard(),
66 position: self.has_position(),
67 };
68 obj.serialize(serializer)
69 }
70 }
71}
72
73impl<'de> Deserialize<'de> for ArrayIndexMode {
74 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
75 where
76 D: Deserializer<'de>,
77 {
78 use serde::de::Error;
79
80 #[derive(Deserialize)]
82 #[serde(untagged)]
83 enum ModeOrPreset {
84 Preset(String),
85 Object(ArrayIndexModeObject),
86 }
87
88 match ModeOrPreset::deserialize(deserializer)? {
89 ModeOrPreset::Preset(s) => match s.to_lowercase().as_str() {
90 "all" => Ok(Self::ALL),
91 "none" => Ok(Self::NONE),
92 other => Err(D::Error::custom(format!(
93 "unknown preset '{}', expected 'all' or 'none'",
94 other
95 ))),
96 },
97 ModeOrPreset::Object(obj) => {
98 let mut mode = Self::NONE;
99 if obj.item {
100 mode |= Self::ITEM;
101 }
102 if obj.wildcard {
103 mode |= Self::WILDCARD;
104 }
105 if obj.position {
106 mode |= Self::POSITION;
107 }
108 Ok(mode)
109 }
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117
118 #[test]
119 fn test_default_is_none() {
120 let mode = ArrayIndexMode::default();
121 assert_eq!(mode, ArrayIndexMode::NONE);
122 assert!(!mode.has_item());
123 assert!(!mode.has_wildcard());
124 assert!(!mode.has_position());
125 }
126
127 #[test]
128 fn test_all_contains_all_flags() {
129 let mode = ArrayIndexMode::ALL;
130 assert!(mode.has_item());
131 assert!(mode.has_wildcard());
132 assert!(mode.has_position());
133 }
134
135 #[test]
136 fn test_individual_flags() {
137 let item = ArrayIndexMode::ITEM;
138 assert!(item.has_item());
139 assert!(!item.has_wildcard());
140 assert!(!item.has_position());
141
142 let wildcard = ArrayIndexMode::WILDCARD;
143 assert!(!wildcard.has_item());
144 assert!(wildcard.has_wildcard());
145 assert!(!wildcard.has_position());
146
147 let position = ArrayIndexMode::POSITION;
148 assert!(!position.has_item());
149 assert!(!position.has_wildcard());
150 assert!(position.has_position());
151 }
152
153 #[test]
154 fn test_flag_combinations() {
155 let combo = ArrayIndexMode::ITEM | ArrayIndexMode::WILDCARD;
156 assert!(combo.has_item());
157 assert!(combo.has_wildcard());
158 assert!(!combo.has_position());
159 }
160
161 #[test]
162 fn test_serialize_all_as_preset() {
163 let mode = ArrayIndexMode::ALL;
164 let json = serde_json::to_string(&mode).unwrap();
165 assert_eq!(json, r#""all""#);
166 }
167
168 #[test]
169 fn test_serialize_none_as_preset() {
170 let mode = ArrayIndexMode::NONE;
171 let json = serde_json::to_string(&mode).unwrap();
172 assert_eq!(json, r#""none""#);
173 }
174
175 #[test]
176 fn test_serialize_partial_as_object() {
177 let mode = ArrayIndexMode::ITEM | ArrayIndexMode::WILDCARD;
178 let json = serde_json::to_string(&mode).unwrap();
179 assert!(json.contains("\"item\":true"));
181 assert!(json.contains("\"wildcard\":true"));
182 assert!(json.contains("\"position\":false"));
183 }
184
185 #[test]
186 fn test_round_trip_none() {
187 let original = ArrayIndexMode::NONE;
188 let json = serde_json::to_string(&original).unwrap();
189 let parsed: ArrayIndexMode = serde_json::from_str(&json).unwrap();
190 assert_eq!(original, parsed);
191 }
192
193 #[test]
194 fn test_round_trip_all() {
195 let original = ArrayIndexMode::ALL;
196 let json = serde_json::to_string(&original).unwrap();
197 let parsed: ArrayIndexMode = serde_json::from_str(&json).unwrap();
198 assert_eq!(original, parsed);
199 }
200
201 #[test]
202 fn test_round_trip_partial() {
203 let original = ArrayIndexMode::ITEM | ArrayIndexMode::WILDCARD;
204 let json = serde_json::to_string(&original).unwrap();
205 let parsed: ArrayIndexMode = serde_json::from_str(&json).unwrap();
206 assert_eq!(original, parsed);
207 }
208
209 #[test]
210 fn test_deserialize_object_form() {
211 let json = r#"{"item":true,"wildcard":true,"position":false}"#;
212 let mode: ArrayIndexMode = serde_json::from_str(json).unwrap();
213 assert!(mode.has_item());
214 assert!(mode.has_wildcard());
215 assert!(!mode.has_position());
216 }
217
218 #[test]
219 fn test_deserialize_preset_all() {
220 let json = r#""all""#;
221 let mode: ArrayIndexMode = serde_json::from_str(json).unwrap();
222 assert_eq!(mode, ArrayIndexMode::ALL);
223 }
224
225 #[test]
226 fn test_deserialize_preset_none() {
227 let json = r#""none""#;
228 let mode: ArrayIndexMode = serde_json::from_str(json).unwrap();
229 assert_eq!(mode, ArrayIndexMode::NONE);
230 }
231
232 #[test]
233 fn test_deserialize_preset_case_insensitive() {
234 let cases = [
235 ("\"ALL\"", ArrayIndexMode::ALL),
236 ("\"None\"", ArrayIndexMode::NONE),
237 ("\"NONE\"", ArrayIndexMode::NONE),
238 ("\"All\"", ArrayIndexMode::ALL),
239 ];
240 for (json, expected) in cases {
241 let mode: ArrayIndexMode = serde_json::from_str(json).unwrap();
242 assert_eq!(mode, expected, "Failed for input: {}", json);
243 }
244 }
245
246 #[test]
247 fn test_deserialize_empty_object_is_none() {
248 let json = r#"{}"#;
249 let mode: ArrayIndexMode = serde_json::from_str(json).unwrap();
250 assert_eq!(mode, ArrayIndexMode::NONE);
251 }
252
253 #[test]
254 fn test_deserialize_partial_object() {
255 let json = r#"{"wildcard":true}"#;
257 let mode: ArrayIndexMode = serde_json::from_str(json).unwrap();
258 assert!(!mode.has_item());
259 assert!(mode.has_wildcard());
260 assert!(!mode.has_position());
261 }
262
263 #[test]
264 fn test_deserialize_invalid_preset_returns_error() {
265 let json = r#""invalid""#;
266 let result: Result<ArrayIndexMode, _> = serde_json::from_str(json);
267 assert!(result.is_err());
268 let err = result.unwrap_err().to_string();
269 assert!(
270 err.contains("unknown preset"),
271 "Error should mention 'unknown preset': {}",
272 err
273 );
274 }
275}