paddle_rust_sdk/
nullable.rs1use serde::Serialize;
2
3#[derive(Clone, Debug, PartialEq)]
12pub enum Nullable<T> {
13 Unchanged,
14 Null,
15 Value(T),
16}
17
18impl<T> Nullable<T> {
19 pub fn is_unchanged(&self) -> bool {
20 matches!(self, Nullable::Unchanged)
21 }
22}
23
24impl<T> Default for Nullable<T> {
25 fn default() -> Self {
26 Nullable::Unchanged
27 }
28}
29
30impl<T> From<T> for Nullable<T> {
31 fn from(value: T) -> Self {
32 Nullable::Value(value)
33 }
34}
35
36impl From<&str> for Nullable<String> {
37 fn from(value: &str) -> Self {
38 Nullable::Value(value.to_string())
39 }
40}
41
42macro_rules! nullable_from_string_like {
43 ($($t:ty),* $(,)?) => {
44 $(
45 impl From<&str> for Nullable<$t> {
46 fn from(s: &str) -> Self {
47 Nullable::Value(s.into())
48 }
49 }
50 impl From<String> for Nullable<$t> {
51 fn from(s: String) -> Self {
52 Nullable::Value(s.into())
53 }
54 }
55 )*
56 };
57}
58
59use paddle_rust_sdk_types::ids::{AddressID, BusinessID, CustomerID, DiscountID};
60nullable_from_string_like!(CustomerID, AddressID, BusinessID, DiscountID);
61
62impl<T: Serialize> Serialize for Nullable<T> {
63 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
64 where
65 S: serde::Serializer,
66 {
67 match self {
68 Nullable::Unchanged => serializer.serialize_none(),
69 Nullable::Null => serializer.serialize_none(),
70 Nullable::Value(v) => v.serialize(serializer),
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[derive(Serialize)]
80 struct TestStruct {
81 #[serde(skip_serializing_if = "Nullable::is_unchanged")]
82 field: Nullable<String>,
83 }
84
85 #[derive(Serialize)]
86 struct MultiField {
87 #[serde(skip_serializing_if = "Nullable::is_unchanged")]
88 a: Nullable<String>,
89 #[serde(skip_serializing_if = "Nullable::is_unchanged")]
90 b: Nullable<i32>,
91 #[serde(skip_serializing_if = "Nullable::is_unchanged")]
92 c: Nullable<bool>,
93 }
94
95 #[test]
96 fn unchanged_is_omitted() {
97 let s = TestStruct {
98 field: Nullable::Unchanged,
99 };
100 let json = serde_json::to_value(&s).unwrap();
101 assert_eq!(json, serde_json::json!({}));
102 }
103
104 #[test]
105 fn null_serializes_as_null() {
106 let s = TestStruct {
107 field: Nullable::Null,
108 };
109 let json = serde_json::to_value(&s).unwrap();
110 assert_eq!(json, serde_json::json!({"field": null}));
111 }
112
113 #[test]
114 fn value_serializes_as_value() {
115 let s = TestStruct {
116 field: Nullable::Value("hello".to_string()),
117 };
118 let json = serde_json::to_value(&s).unwrap();
119 assert_eq!(json, serde_json::json!({"field": "hello"}));
120 }
121
122 #[test]
123 fn mixed_fields() {
124 let s = MultiField {
125 a: Nullable::Unchanged,
126 b: Nullable::Null,
127 c: Nullable::Value(true),
128 };
129 let json = serde_json::to_value(&s).unwrap();
130 assert_eq!(json, serde_json::json!({"b": null, "c": true}));
131 }
132
133 #[test]
134 fn default_is_unchanged() {
135 let n: Nullable<String> = Nullable::default();
136 assert!(n.is_unchanged());
137 }
138
139 #[test]
140 fn from_value() {
141 let n: Nullable<String> = "hello".to_string().into();
142 assert_eq!(n, Nullable::Value("hello".to_string()));
143 }
144}