1#![cfg_attr(not(any(test, feature = "box", feature = "option")), no_std)]
47
48#[doc(hidden)]
49pub use struct_patch_derive::Patch;
50#[cfg(any(feature = "box", feature = "option"))]
51pub mod std;
52pub mod traits;
53pub use traits::*;
54
55#[cfg(test)]
56mod tests {
57 use serde::Deserialize;
58 #[cfg(feature = "merge")]
59 use struct_patch::Merge;
60 use struct_patch::Patch;
61 #[cfg(feature = "status")]
62 use struct_patch::PatchStatus;
63
64 use crate as struct_patch;
65
66 #[test]
67 fn test_basic() {
68 #[derive(Patch, Debug, PartialEq)]
69 struct Item {
70 field: u32,
71 other: String,
72 }
73
74 let mut item = Item {
75 field: 1,
76 other: String::from("hello"),
77 };
78 let patch = ItemPatch {
79 field: None,
80 other: Some(String::from("bye")),
81 };
82
83 item.apply(patch);
84 assert_eq!(
85 item,
86 Item {
87 field: 1,
88 other: String::from("bye")
89 }
90 );
91 }
92
93 #[test]
94 #[cfg(feature = "status")]
95 fn test_empty() {
96 #[derive(Patch)]
97 #[patch(attribute(derive(Debug, PartialEq)))]
98 struct Item {
99 data: u32,
100 }
101
102 let patch = ItemPatch { data: None };
103 let other_patch = Item::new_empty_patch();
104 assert!(patch.is_empty());
105 assert_eq!(patch, other_patch);
106 let patch = ItemPatch { data: Some(0) };
107 assert!(!patch.is_empty());
108 }
109
110 #[test]
111 fn test_derive() {
112 #[allow(dead_code)]
113 #[derive(Patch)]
114 #[patch(attribute(derive(Copy, Clone, PartialEq, Debug)))]
115 struct Item;
116
117 let patch = ItemPatch {};
118 let other_patch = patch;
119 assert_eq!(patch, other_patch);
120 }
121
122 #[test]
123 fn test_name() {
124 #[derive(Patch)]
125 #[patch(name = "PatchItem")]
126 struct Item;
127
128 let patch = PatchItem {};
129 let mut item = Item;
130 item.apply(patch);
131 }
132
133 #[test]
134 fn test_nullable() {
135 #[derive(Patch, Debug, PartialEq)]
136 struct Item {
137 field: Option<u32>,
138 other: Option<String>,
139 }
140
141 let mut item = Item {
142 field: Some(1),
143 other: Some(String::from("hello")),
144 };
145 let patch = ItemPatch {
146 field: None,
147 other: Some(None),
148 };
149
150 item.apply(patch);
151 assert_eq!(
152 item,
153 Item {
154 field: Some(1),
155 other: None
156 }
157 );
158 }
159
160 #[test]
161 fn test_skip() {
162 #[derive(Patch, PartialEq, Debug)]
163 #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
164 struct Item {
165 #[patch(skip)]
166 id: u32,
167 data: u32,
168 }
169
170 let mut item = Item { id: 1, data: 2 };
171 let data = r#"{ "id": 10, "data": 15 }"#; let patch: ItemPatch = serde_json::from_str(data).unwrap();
173 assert_eq!(patch, ItemPatch { data: Some(15) });
174
175 item.apply(patch);
176 assert_eq!(item, Item { id: 1, data: 15 });
177 }
178
179 #[test]
180 fn test_nested() {
181 #[derive(PartialEq, Debug, Patch, Deserialize)]
182 #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
183 struct B {
184 c: u32,
185 d: u32,
186 }
187
188 #[derive(PartialEq, Debug, Patch, Deserialize)]
189 #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
190 struct A {
191 #[patch(name = "BPatch")]
192 b: B,
193 }
194
195 let mut a = A {
196 b: B { c: 0, d: 0 },
197 };
198 let data = r#"{ "b": { "c": 1 } }"#;
199 let patch: APatch = serde_json::from_str(data).unwrap();
200 a.apply(patch);
207 assert_eq!(
208 a,
209 A {
210 b: B { c: 1, d: 0 }
211 }
212 );
213 }
214
215 #[test]
216 fn test_generic() {
217 #[derive(Patch)]
218 struct Item<T>
219 where
220 T: PartialEq,
221 {
222 pub field: T,
223 }
224
225 let patch = ItemPatch {
226 field: Some(String::from("hello")),
227 };
228 let mut item = Item {
229 field: String::new(),
230 };
231 item.apply(patch);
232 assert_eq!(item.field, "hello");
233 }
234
235 #[test]
236 fn test_named_generic() {
237 #[derive(Patch)]
238 #[patch(name = "PatchItem")]
239 struct Item<T>
240 where
241 T: PartialEq,
242 {
243 pub field: T,
244 }
245
246 let patch = PatchItem {
247 field: Some(String::from("hello")),
248 };
249 let mut item = Item {
250 field: String::new(),
251 };
252 item.apply(patch);
253 }
254
255 #[test]
256 fn test_nested_generic() {
257 #[derive(PartialEq, Debug, Patch, Deserialize)]
258 #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
259 struct B<T>
260 where
261 T: PartialEq,
262 {
263 c: T,
264 d: T,
265 }
266
267 #[derive(PartialEq, Debug, Patch, Deserialize)]
268 #[patch(attribute(derive(PartialEq, Debug, Deserialize)))]
269 struct A {
270 #[patch(name = "BPatch<u32>")]
271 b: B<u32>,
272 }
273
274 let mut a = A {
275 b: B { c: 0, d: 0 },
276 };
277 let data = r#"{ "b": { "c": 1 } }"#;
278 let patch: APatch = serde_json::from_str(data).unwrap();
279
280 a.apply(patch);
281 assert_eq!(
282 a,
283 A {
284 b: B { c: 1, d: 0 }
285 }
286 );
287 }
288
289 #[cfg(feature = "op")]
290 #[test]
291 fn test_shl() {
292 #[derive(Patch, Debug, PartialEq)]
293 struct Item {
294 field: u32,
295 other: String,
296 }
297
298 let item = Item {
299 field: 1,
300 other: String::from("hello"),
301 };
302 let patch = ItemPatch {
303 field: None,
304 other: Some(String::from("bye")),
305 };
306
307 assert_eq!(
308 item << patch,
309 Item {
310 field: 1,
311 other: String::from("bye")
312 }
313 );
314 }
315
316 #[cfg(all(feature = "op", feature = "merge"))]
317 #[test]
318 fn test_shl_on_patch() {
319 #[derive(Patch, Debug, PartialEq)]
320 struct Item {
321 field: u32,
322 other: String,
323 }
324
325 let mut item = Item {
326 field: 1,
327 other: String::from("hello"),
328 };
329 let patch = ItemPatch {
330 field: None,
331 other: Some(String::from("bye")),
332 };
333 let patch2 = ItemPatch {
334 field: Some(2),
335 other: None,
336 };
337
338 let new_patch = patch << patch2;
339
340 item.apply(new_patch);
341 assert_eq!(
342 item,
343 Item {
344 field: 2,
345 other: String::from("bye")
346 }
347 );
348 }
349
350 #[cfg(feature = "op")]
351 #[test]
352 fn test_add_patches() {
353 #[derive(Patch)]
354 #[patch(attribute(derive(Debug, PartialEq)))]
355 struct Item {
356 field: u32,
357 other: String,
358 }
359
360 let patch = ItemPatch {
361 field: Some(1),
362 other: None,
363 };
364 let patch2 = ItemPatch {
365 field: None,
366 other: Some(String::from("hello")),
367 };
368 let overall_patch = patch + patch2;
369 assert_eq!(
370 overall_patch,
371 ItemPatch {
372 field: Some(1),
373 other: Some(String::from("hello")),
374 }
375 );
376 }
377
378 #[cfg(feature = "op")]
379 #[test]
380 #[should_panic]
381 fn test_add_conflict_patches_panic() {
382 #[derive(Patch, Debug, PartialEq)]
383 struct Item {
384 field: u32,
385 }
386
387 let patch = ItemPatch { field: Some(1) };
388 let patch2 = ItemPatch { field: Some(2) };
389 let _overall_patch = patch + patch2;
390 }
391
392 #[cfg(feature = "merge")]
393 #[test]
394 fn test_merge() {
395 #[derive(Patch)]
396 #[patch(attribute(derive(PartialEq, Debug)))]
397 struct Item {
398 a: u32,
399 b: u32,
400 c: u32,
401 d: u32,
402 }
403
404 let patch = ItemPatch {
405 a: None,
406 b: Some(2),
407 c: Some(0),
408 d: None,
409 };
410 let patch2 = ItemPatch {
411 a: Some(1),
412 b: None,
413 c: Some(3),
414 d: None,
415 };
416
417 let merged_patch = patch.merge(patch2);
418 assert_eq!(
419 merged_patch,
420 ItemPatch {
421 a: Some(1),
422 b: Some(2),
423 c: Some(3),
424 d: None,
425 }
426 );
427 }
428
429 #[cfg(feature = "merge")]
430 #[test]
431 fn test_merge_nested() {
432 #[derive(Patch, PartialEq, Debug)]
433 #[patch(attribute(derive(PartialEq, Debug, Clone)))]
434 struct B {
435 c: u32,
436 d: u32,
437 e: u32,
438 f: u32,
439 }
440
441 #[derive(Patch)]
442 #[patch(attribute(derive(PartialEq, Debug)))]
443 struct A {
444 a: u32,
445 #[patch(name = "BPatch")]
446 b: B,
447 }
448
449 let patches = vec![
450 APatch {
451 a: Some(1),
452 b: Some(BPatch {
453 c: None,
454 d: Some(2),
455 e: Some(0),
456 f: None,
457 }),
458 },
459 APatch {
460 a: Some(0),
461 b: Some(BPatch {
462 c: Some(1),
463 d: None,
464 e: Some(3),
465 f: None,
466 }),
467 },
468 ];
469
470 let merged_patch = patches.into_iter().reduce(Merge::merge).unwrap();
471
472 assert_eq!(
473 merged_patch,
474 APatch {
475 a: Some(0),
476 b: Some(BPatch {
477 c: Some(1),
478 d: Some(2),
479 e: Some(3),
480 f: None,
481 }),
482 }
483 );
484 }
485}