agent_client_protocol_schema/
maybe_undefined.rs1use std::{
3 borrow::Cow,
4 ffi::OsStr,
5 ops::Deref,
6 path::{Path, PathBuf},
7 sync::Arc,
8};
9
10use schemars::JsonSchema;
11use serde::{Deserialize, Deserializer, Serialize, Serializer};
12
13#[allow(missing_docs)]
31#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, JsonSchema)]
32#[schemars(with = "Option<Option<T>>", inline)]
33#[expect(clippy::exhaustive_enums)]
34pub enum MaybeUndefined<T> {
35 #[default]
36 Undefined,
37 Null,
38 Value(T),
39}
40
41impl<T> MaybeUndefined<T> {
42 #[inline]
44 pub const fn is_undefined(&self) -> bool {
45 matches!(self, MaybeUndefined::Undefined)
46 }
47
48 #[inline]
50 pub const fn is_null(&self) -> bool {
51 matches!(self, MaybeUndefined::Null)
52 }
53
54 #[inline]
56 pub const fn is_value(&self) -> bool {
57 matches!(self, MaybeUndefined::Value(_))
58 }
59
60 #[inline]
63 pub const fn value(&self) -> Option<&T> {
64 match self {
65 MaybeUndefined::Value(value) => Some(value),
66 _ => None,
67 }
68 }
69
70 #[inline]
72 pub fn take(self) -> Option<T> {
73 match self {
74 MaybeUndefined::Value(value) => Some(value),
75 _ => None,
76 }
77 }
78
79 #[inline]
81 pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
82 match self {
83 MaybeUndefined::Undefined => None,
84 MaybeUndefined::Null => Some(None),
85 MaybeUndefined::Value(value) => Some(Some(value)),
86 }
87 }
88
89 #[inline]
91 pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
92 where
93 U: ?Sized,
94 T: Deref<Target = U>,
95 {
96 match self {
97 MaybeUndefined::Undefined => None,
98 MaybeUndefined::Null => Some(None),
99 MaybeUndefined::Value(value) => Some(Some(&**value)),
100 }
101 }
102
103 #[inline]
105 pub fn contains_value<U>(&self, x: &U) -> bool
106 where
107 U: PartialEq<T>,
108 {
109 match self {
110 MaybeUndefined::Value(y) => x == y,
111 _ => false,
112 }
113 }
114
115 #[inline]
118 pub fn contains<U>(&self, x: Option<&U>) -> bool
119 where
120 U: PartialEq<T>,
121 {
122 match self {
123 MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
124 MaybeUndefined::Null => x.is_none(),
125 MaybeUndefined::Undefined => false,
126 }
127 }
128
129 #[inline]
132 pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
133 match self {
134 MaybeUndefined::Value(v) => match f(Some(v)) {
135 Some(v) => MaybeUndefined::Value(v),
136 None => MaybeUndefined::Null,
137 },
138 MaybeUndefined::Null => match f(None) {
139 Some(v) => MaybeUndefined::Value(v),
140 None => MaybeUndefined::Null,
141 },
142 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
143 }
144 }
145
146 #[inline]
149 pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
150 match self {
151 MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
152 MaybeUndefined::Null => MaybeUndefined::Null,
153 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
154 }
155 }
156
157 pub fn update_to(self, value: &mut Option<T>) {
176 match self {
177 MaybeUndefined::Value(new) => *value = Some(new),
178 MaybeUndefined::Null => *value = None,
179 MaybeUndefined::Undefined => {}
180 }
181 }
182}
183
184impl<T, E> MaybeUndefined<Result<T, E>> {
185 #[inline]
199 pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
200 match self {
201 MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
202 MaybeUndefined::Null => Ok(MaybeUndefined::Null),
203 MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
204 MaybeUndefined::Value(Err(e)) => Err(e),
205 }
206 }
207}
208
209impl<T: Serialize> Serialize for MaybeUndefined<T> {
210 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
211 match self {
212 MaybeUndefined::Value(value) => value.serialize(serializer),
213 MaybeUndefined::Null => serializer.serialize_none(),
214 MaybeUndefined::Undefined => serializer.serialize_unit(),
215 }
216 }
217}
218
219impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
220where
221 T: Deserialize<'de>,
222{
223 fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
224 where
225 D: Deserializer<'de>,
226 {
227 Option::<T>::deserialize(deserializer).map(|value| match value {
228 Some(value) => MaybeUndefined::Value(value),
229 None => MaybeUndefined::Null,
230 })
231 }
232}
233
234impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
235 fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
236 match maybe_undefined {
237 MaybeUndefined::Undefined => None,
238 MaybeUndefined::Null => Some(None),
239 MaybeUndefined::Value(value) => Some(Some(value)),
240 }
241 }
242}
243
244impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
245 fn from(value: Option<Option<T>>) -> Self {
246 match value {
247 Some(Some(value)) => Self::Value(value),
248 Some(None) => Self::Null,
249 None => Self::Undefined,
250 }
251 }
252}
253
254pub trait IntoMaybeUndefined<T> {
258 fn into_maybe_undefined(self) -> MaybeUndefined<T>;
259}
260
261impl<T> IntoMaybeUndefined<T> for T {
262 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
263 MaybeUndefined::Value(self)
264 }
265}
266
267impl<T> IntoMaybeUndefined<T> for Option<T> {
268 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
269 match self {
270 Some(value) => MaybeUndefined::Value(value),
271 None => MaybeUndefined::Null,
272 }
273 }
274}
275
276impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
277 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
278 self
279 }
280}
281
282impl IntoMaybeUndefined<String> for &str {
283 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
284 MaybeUndefined::Value(self.into())
285 }
286}
287
288impl IntoMaybeUndefined<String> for &mut str {
289 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
290 MaybeUndefined::Value(self.into())
291 }
292}
293
294impl IntoMaybeUndefined<String> for &String {
295 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
296 MaybeUndefined::Value(self.into())
297 }
298}
299
300impl IntoMaybeUndefined<String> for Box<str> {
301 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
302 MaybeUndefined::Value(self.into())
303 }
304}
305
306impl IntoMaybeUndefined<String> for Cow<'_, str> {
307 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
308 MaybeUndefined::Value(self.into())
309 }
310}
311
312impl IntoMaybeUndefined<String> for Arc<str> {
313 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
314 MaybeUndefined::Value(self.to_string())
315 }
316}
317
318impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
319 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
320 MaybeUndefined::Value(self.into())
321 }
322}
323
324impl IntoMaybeUndefined<PathBuf> for Box<Path> {
325 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
326 MaybeUndefined::Value(self.into())
327 }
328}
329
330impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
331 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
332 MaybeUndefined::Value(self.into())
333 }
334}
335
336impl IntoMaybeUndefined<serde_json::Value> for &str {
337 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
338 MaybeUndefined::Value(self.into())
339 }
340}
341
342impl IntoMaybeUndefined<serde_json::Value> for String {
343 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
344 MaybeUndefined::Value(self.into())
345 }
346}
347
348impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
349 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
350 MaybeUndefined::Value(self.into())
351 }
352}
353
354#[cfg(test)]
355mod tests {
356 use serde::{Deserialize, Serialize};
357 use serde_json::{from_value, json, to_value};
358
359 use super::*;
360
361 #[test]
362 fn test_maybe_undefined_serde() {
363 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
364 struct A {
365 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
366 a: MaybeUndefined<i32>,
367 }
368
369 assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
370
371 assert_eq!(
372 from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
373 MaybeUndefined::Value(100)
374 );
375 assert_eq!(
376 from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
377 MaybeUndefined::Null
378 );
379
380 assert_eq!(
381 to_value(&A {
382 a: MaybeUndefined::Value(100i32)
383 })
384 .unwrap(),
385 json!({"a": 100})
386 );
387
388 assert_eq!(
389 to_value(&A {
390 a: MaybeUndefined::Null,
391 })
392 .unwrap(),
393 json!({ "a": null })
394 );
395
396 assert_eq!(
397 to_value(&A {
398 a: MaybeUndefined::Undefined,
399 })
400 .unwrap(),
401 json!({})
402 );
403
404 assert_eq!(
405 from_value::<A>(json!({"a": 100})).unwrap(),
406 A {
407 a: MaybeUndefined::Value(100i32)
408 }
409 );
410
411 assert_eq!(
412 from_value::<A>(json!({ "a": null })).unwrap(),
413 A {
414 a: MaybeUndefined::Null
415 }
416 );
417
418 assert_eq!(
419 from_value::<A>(json!({})).unwrap(),
420 A {
421 a: MaybeUndefined::Undefined
422 }
423 );
424 }
425
426 #[test]
427 fn test_maybe_undefined_to_nested_option() {
428 assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
429
430 assert_eq!(
431 Option::<Option<i32>>::from(MaybeUndefined::Null),
432 Some(None)
433 );
434
435 assert_eq!(
436 Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
437 Some(Some(42))
438 );
439 }
440
441 #[test]
442 fn test_as_opt_ref() {
443 let value = MaybeUndefined::<String>::Undefined;
444 let r = value.as_opt_ref();
445 assert_eq!(r, None);
446
447 let value = MaybeUndefined::<String>::Null;
448 let r = value.as_opt_ref();
449 assert_eq!(r, Some(None));
450
451 let value = MaybeUndefined::<String>::Value("abc".to_string());
452 let r = value.as_opt_ref();
453 assert_eq!(r, Some(Some(&"abc".to_string())));
454 }
455
456 #[test]
457 fn test_as_opt_deref() {
458 let value = MaybeUndefined::<String>::Undefined;
459 let r = value.as_opt_deref();
460 assert_eq!(r, None);
461
462 let value = MaybeUndefined::<String>::Null;
463 let r = value.as_opt_deref();
464 assert_eq!(r, Some(None));
465
466 let value = MaybeUndefined::<String>::Value("abc".to_string());
467 let r = value.as_opt_deref();
468 assert_eq!(r, Some(Some("abc")));
469 }
470
471 #[test]
472 fn test_contains_value() {
473 let test = "abc";
474
475 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
476 assert!(!value.contains_value(&test));
477
478 value = MaybeUndefined::Null;
479 assert!(!value.contains_value(&test));
480
481 value = MaybeUndefined::Value("abc".to_string());
482 assert!(value.contains_value(&test));
483 }
484
485 #[test]
486 fn test_contains() {
487 let test = Some("abc");
488 let none: Option<&str> = None;
489
490 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
491 assert!(!value.contains(test.as_ref()));
492 assert!(!value.contains(none.as_ref()));
493
494 value = MaybeUndefined::Null;
495 assert!(!value.contains(test.as_ref()));
496 assert!(value.contains(none.as_ref()));
497
498 value = MaybeUndefined::Value("abc".to_string());
499 assert!(value.contains(test.as_ref()));
500 assert!(!value.contains(none.as_ref()));
501 }
502
503 #[test]
504 fn test_map_value() {
505 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
506 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
507
508 value = MaybeUndefined::Null;
509 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
510
511 value = MaybeUndefined::Value(5);
512 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
513 }
514
515 #[test]
516 fn test_map() {
517 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
518 assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
519
520 value = MaybeUndefined::Null;
521 assert_eq!(
522 value.map(|v| Some(v.is_some())),
523 MaybeUndefined::Value(false)
524 );
525
526 value = MaybeUndefined::Value(5);
527 assert_eq!(
528 value.map(|v| Some(v.is_some())),
529 MaybeUndefined::Value(true)
530 );
531 }
532
533 #[test]
534 fn test_transpose() {
535 let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
536 assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
537
538 value = MaybeUndefined::Null;
539 assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
540
541 value = MaybeUndefined::Value(Ok(5));
542 assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
543
544 value = MaybeUndefined::Value(Err("error"));
545 assert_eq!(value.transpose(), Err("error"));
546 }
547}