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