|
3 | 3 | //! ## Types |
4 | 4 | //! |
5 | 5 | //! - [`MaybeUndefined<T>`] — three-state: undefined (key absent), null, or value. |
6 | | -//! - [`RequiredNullable<T>`] — required-but-nullable: key must be present, value may be null. |
7 | 6 | //! - [`SkipListener`] — [`serde_with::InspectError`] hook used by every |
8 | 7 | //! `VecSkipError` call site in the protocol types. |
9 | 8 | //! |
@@ -630,140 +629,6 @@ impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> { |
630 | 629 | } |
631 | 630 | } |
632 | 631 |
|
633 | | -// ---- RequiredNullable<T> ---- |
634 | | - |
635 | | -/// A value that must be present on the wire but whose value may be `null`. |
636 | | -/// |
637 | | -/// Unlike `Option<T>`, which serde treats as an implicitly optional field |
638 | | -/// (defaulting to `None` when absent), `RequiredNullable<T>` requires the key to be |
639 | | -/// present during deserialization. A missing field will produce a |
640 | | -/// deserialization error rather than silently defaulting to `None`. |
641 | | -/// |
642 | | -/// On the wire this serializes identically to `Option<T>` — either `null` or |
643 | | -/// the JSON representation of `T`. |
644 | | -/// |
645 | | -/// **Note:** The `Deserialize` impl uses `serde_json::Value` internally to |
646 | | -/// enforce the "required" constraint, so this type is JSON-only. |
647 | | -/// |
648 | | -/// # Example |
649 | | -/// |
650 | | -/// ```rust |
651 | | -/// use agent_client_protocol_schema::RequiredNullable; |
652 | | -/// use serde::{Serialize, Deserialize}; |
653 | | -/// |
654 | | -/// #[derive(Serialize, Deserialize, Debug, PartialEq)] |
655 | | -/// struct Config { |
656 | | -/// // MUST be present in JSON, but its value can be null |
657 | | -/// value: RequiredNullable<String>, |
658 | | -/// } |
659 | | -/// |
660 | | -/// // ✅ Present with a value |
661 | | -/// let c: Config = serde_json::from_str(r#"{"value":"hello"}"#).unwrap(); |
662 | | -/// assert_eq!(c.value, RequiredNullable::new("hello".to_string())); |
663 | | -/// |
664 | | -/// // ✅ Present as null |
665 | | -/// let c: Config = serde_json::from_str(r#"{"value":null}"#).unwrap(); |
666 | | -/// assert_eq!(c.value, RequiredNullable::null()); |
667 | | -/// |
668 | | -/// // ❌ Missing key — deserialization error |
669 | | -/// assert!(serde_json::from_str::<Config>(r#"{}"#).is_err()); |
670 | | -/// ``` |
671 | | -#[cfg(feature = "unstable_llm_providers")] |
672 | | -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, JsonSchema)] |
673 | | -#[schemars(with = "Option<T>", inline)] |
674 | | -#[non_exhaustive] |
675 | | -pub struct RequiredNullable<T>(pub Option<T>); |
676 | | - |
677 | | -#[cfg(feature = "unstable_llm_providers")] |
678 | | -impl<T> Default for RequiredNullable<T> { |
679 | | - fn default() -> Self { |
680 | | - Self(None) |
681 | | - } |
682 | | -} |
683 | | - |
684 | | -#[cfg(feature = "unstable_llm_providers")] |
685 | | -impl<T: Serialize> Serialize for RequiredNullable<T> { |
686 | | - fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { |
687 | | - self.0.serialize(serializer) |
688 | | - } |
689 | | -} |
690 | | - |
691 | | -#[cfg(feature = "unstable_llm_providers")] |
692 | | -impl<'de, T: Deserialize<'de>> Deserialize<'de> for RequiredNullable<T> { |
693 | | - fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> { |
694 | | - // Deserialize via serde_json::Value so that `deserialize_any` is called. |
695 | | - // serde's MissingFieldDeserializer errors on `deserialize_any` (good — the |
696 | | - // field is required), whereas `deserialize_option` silently returns None. |
697 | | - let value = serde_json::Value::deserialize(deserializer)?; |
698 | | - if value.is_null() { |
699 | | - Ok(RequiredNullable(None)) |
700 | | - } else { |
701 | | - T::deserialize(value) |
702 | | - .map(RequiredNullable::new) |
703 | | - .map_err(serde::de::Error::custom) |
704 | | - } |
705 | | - } |
706 | | -} |
707 | | - |
708 | | -#[cfg(feature = "unstable_llm_providers")] |
709 | | -impl<T> RequiredNullable<T> { |
710 | | - /// Creates a `RequiredNullable` containing a value. |
711 | | - #[must_use] |
712 | | - pub fn new(value: T) -> Self { |
713 | | - Self(Some(value)) |
714 | | - } |
715 | | - |
716 | | - /// Creates a `RequiredNullable` representing `null`. |
717 | | - #[must_use] |
718 | | - pub fn null() -> Self { |
719 | | - Self(None) |
720 | | - } |
721 | | - |
722 | | - /// Returns `true` if the value is `null`. |
723 | | - #[must_use] |
724 | | - pub fn is_null(&self) -> bool { |
725 | | - self.0.is_none() |
726 | | - } |
727 | | - |
728 | | - /// Returns `true` if the value is present (not null). |
729 | | - #[must_use] |
730 | | - pub fn is_value(&self) -> bool { |
731 | | - self.0.is_some() |
732 | | - } |
733 | | - |
734 | | - /// Returns a reference to the contained value, if present. |
735 | | - #[must_use] |
736 | | - pub fn value(&self) -> Option<&T> { |
737 | | - self.0.as_ref() |
738 | | - } |
739 | | - |
740 | | - /// Returns a mutable reference to the contained value, if present. |
741 | | - #[must_use] |
742 | | - pub fn value_mut(&mut self) -> Option<&mut T> { |
743 | | - self.0.as_mut() |
744 | | - } |
745 | | - |
746 | | - /// Converts into the inner `Option<T>`. |
747 | | - #[must_use] |
748 | | - pub fn into_inner(self) -> Option<T> { |
749 | | - self.0 |
750 | | - } |
751 | | -} |
752 | | - |
753 | | -#[cfg(feature = "unstable_llm_providers")] |
754 | | -impl<T> From<Option<T>> for RequiredNullable<T> { |
755 | | - fn from(value: Option<T>) -> Self { |
756 | | - Self(value) |
757 | | - } |
758 | | -} |
759 | | - |
760 | | -#[cfg(feature = "unstable_llm_providers")] |
761 | | -impl<T> From<RequiredNullable<T>> for Option<T> { |
762 | | - fn from(value: RequiredNullable<T>) -> Self { |
763 | | - value.0 |
764 | | - } |
765 | | -} |
766 | | - |
767 | 632 | #[cfg(test)] |
768 | 633 | mod tests { |
769 | 634 | use serde::{Deserialize, Serialize}; |
@@ -957,89 +822,4 @@ mod tests { |
957 | 822 | value = MaybeUndefined::Value(Err("error")); |
958 | 823 | assert_eq!(value.transpose(), Err("error")); |
959 | 824 | } |
960 | | - |
961 | | - // ---- RequiredNullable tests ---- |
962 | | - |
963 | | - #[cfg(feature = "unstable_llm_providers")] |
964 | | - mod nullable_tests { |
965 | | - use super::*; |
966 | | - use serde_json::from_str; |
967 | | - |
968 | | - #[derive(Serialize, Deserialize, Debug, PartialEq)] |
969 | | - struct Example { |
970 | | - value: RequiredNullable<String>, |
971 | | - } |
972 | | - |
973 | | - #[test] |
974 | | - fn present_with_value() { |
975 | | - let example: Example = from_str(r#"{"value":"hello"}"#).unwrap(); |
976 | | - assert_eq!(example.value, RequiredNullable(Some("hello".to_string()))); |
977 | | - } |
978 | | - |
979 | | - #[test] |
980 | | - fn present_as_null() { |
981 | | - let example: Example = from_str(r#"{"value":null}"#).unwrap(); |
982 | | - assert_eq!(example.value, RequiredNullable(None)); |
983 | | - } |
984 | | - |
985 | | - #[test] |
986 | | - fn missing_key_fails() { |
987 | | - assert!(from_str::<Example>(r"{}").is_err()); |
988 | | - } |
989 | | - |
990 | | - #[test] |
991 | | - fn serialize_value() { |
992 | | - let example = Example { |
993 | | - value: RequiredNullable(Some("hello".to_string())), |
994 | | - }; |
995 | | - assert_eq!(to_value(&example).unwrap(), json!({"value": "hello"})); |
996 | | - } |
997 | | - |
998 | | - #[test] |
999 | | - fn serialize_null() { |
1000 | | - let example = Example { |
1001 | | - value: RequiredNullable(None), |
1002 | | - }; |
1003 | | - assert_eq!(to_value(&example).unwrap(), json!({"value": null})); |
1004 | | - } |
1005 | | - |
1006 | | - #[test] |
1007 | | - fn from_option() { |
1008 | | - let nullable: RequiredNullable<i32> = Some(42).into(); |
1009 | | - assert_eq!(nullable, RequiredNullable(Some(42))); |
1010 | | - |
1011 | | - let nullable: RequiredNullable<i32> = None.into(); |
1012 | | - assert_eq!(nullable, RequiredNullable(None)); |
1013 | | - } |
1014 | | - |
1015 | | - #[test] |
1016 | | - fn into_option() { |
1017 | | - let option: Option<i32> = RequiredNullable(Some(42)).into(); |
1018 | | - assert_eq!(option, Some(42)); |
1019 | | - |
1020 | | - let option: Option<i32> = RequiredNullable(None).into(); |
1021 | | - assert_eq!(option, None); |
1022 | | - } |
1023 | | - |
1024 | | - #[test] |
1025 | | - fn methods() { |
1026 | | - let value = RequiredNullable::new(42); |
1027 | | - assert!(value.is_value()); |
1028 | | - assert!(!value.is_null()); |
1029 | | - assert_eq!(value.value(), Some(&42)); |
1030 | | - assert_eq!(value.into_inner(), Some(42)); |
1031 | | - |
1032 | | - let null: RequiredNullable<i32> = RequiredNullable::null(); |
1033 | | - assert!(!null.is_value()); |
1034 | | - assert!(null.is_null()); |
1035 | | - assert_eq!(null.value(), None); |
1036 | | - assert_eq!(null.into_inner(), None); |
1037 | | - } |
1038 | | - |
1039 | | - #[test] |
1040 | | - fn default_is_null() { |
1041 | | - let nullable: RequiredNullable<i32> = RequiredNullable::default(); |
1042 | | - assert_eq!(nullable, RequiredNullable(None)); |
1043 | | - } |
1044 | | - } |
1045 | 825 | } |
0 commit comments