1#[derive(Default, Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
11pub enum Patch<T> {
12 #[default]
14 Ignore,
15 Update(T),
17}
18
19impl<T> Patch<T> {
20 pub const fn update(value: T) -> Self {
22 Self::Update(value)
23 }
24
25 pub const fn ignore() -> Self {
27 Self::Ignore
28 }
29
30 pub const fn is_ignore(&self) -> bool {
31 matches!(self, Self::Ignore)
32 }
33
34 pub const fn as_ref(&self) -> Patch<&T> {
36 match self {
37 Self::Update(value) => Patch::Update(value),
38 Self::Ignore => Patch::Ignore,
39 }
40 }
41
42 pub const fn as_option_ref(&self) -> Option<&T> {
44 match self {
45 Self::Update(value) => Some(value),
46 Self::Ignore => None,
47 }
48 }
49
50 pub fn as_option(&self) -> Option<T>
52 where
53 T: Clone,
54 {
55 match self {
56 Self::Update(value) => Some(value.clone()),
57 Self::Ignore => None,
58 }
59 }
60
61 pub fn into_option(self) -> Option<T> {
63 match self {
64 Self::Update(value) => Some(value),
65 Self::Ignore => None,
66 }
67 }
68}
69
70impl<T> From<Patch<T>> for Option<T> {
71 fn from(value: Patch<T>) -> Self {
72 value.into_option()
73 }
74}
75
76impl<T> From<Option<T>> for Patch<T> {
77 fn from(value: Option<T>) -> Self {
78 value.map_or_else(|| Self::Ignore, |value| Self::Update(value))
79 }
80}
81
82impl<T> PartialEq<Option<T>> for Patch<T>
83where
84 T: PartialEq,
85{
86 fn eq(&self, other: &Option<T>) -> bool {
87 match (self, other) {
88 (Self::Update(value), Some(other)) => value == other,
89 (Self::Ignore, None) => true,
90 _ => false,
91 }
92 }
93}
94
95#[cfg(feature = "serde")]
96mod serde {
97 use super::Patch;
98 use serde::{Deserialize, Serialize};
99
100 impl<T> Serialize for Patch<T>
101 where
102 T: Serialize,
103 {
104 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
105 where
106 S: serde::Serializer,
107 {
108 match self {
109 Self::Update(v) => serializer.serialize_some(v),
110 Self::Ignore => serializer.serialize_none(),
111 }
112 }
113 }
114
115 impl<'de, T> Deserialize<'de> for Patch<T>
116 where
117 T: Deserialize<'de>,
118 {
119 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120 where
121 D: serde::Deserializer<'de>,
122 {
123 let opt = Option::<T>::deserialize(deserializer)?;
125 Ok(opt.map_or_else(|| Self::Ignore, |v| Self::Update(v)))
126 }
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn test_basic_functionality() {
136 let update = Patch::update(42);
137 let ignore: Patch<i32> = Patch::ignore();
138
139 assert!(matches!(update, Patch::Update(42)));
140 assert!(matches!(ignore, Patch::Ignore));
141 }
142
143 #[test]
144 fn test_option_conversions() {
145 let update: Patch<i32> = Patch::update(42);
146 let ignore: Patch<i32> = Patch::ignore();
147
148 assert_eq!(Option::from(update), Some(42));
149 assert_eq!(Option::from(ignore), None::<i32>);
150
151 assert_eq!(Patch::from(Some(42)), Patch::Update(42));
152 assert_eq!(Patch::from(None::<i32>), Patch::Ignore);
153 }
154
155 #[test]
156 fn test_reference_operations() {
157 let update = Patch::update(42);
158 let ignore: Patch<i32> = Patch::ignore();
159
160 assert_eq!(update.as_ref(), Patch::Update(&42));
161 assert_eq!(ignore.as_ref(), Patch::Ignore);
162
163 assert_eq!(update.as_option_ref(), Some(&42));
164 assert_eq!(ignore.as_option_ref(), None);
165 }
166
167 #[test]
168 #[allow(clippy::default_trait_access)]
169 fn test_default() {
170 let patch: Patch<i32> = Default::default();
171 assert!(matches!(patch, Patch::Ignore));
172 }
173
174 #[test]
175 fn test_equality() {
176 let update = Patch::update(42);
177 let ignore: Patch<i32> = Patch::ignore();
178
179 assert_eq!(update, Some(42));
180 assert_ne!(update, None);
181 assert_eq!(ignore, None);
182 assert_ne!(ignore, Some(42));
183 }
184}