├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── examples ├── enum.rs ├── map.rs ├── nested_struct.rs ├── simple.rs └── struct.rs ├── serde-diff-derive ├── Cargo.toml ├── README.md └── src │ ├── lib.rs │ └── serde_diff │ ├── args.rs │ └── mod.rs └── src ├── apply.rs ├── config.rs ├── counting_serializer.rs ├── difference.rs ├── implementation.rs ├── lib.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | **/*.rs.bk 3 | Cargo.lock 4 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | script: 7 | - | 8 | if [[ $TRAVIS_RUST_VERSION == *stable* ]] 9 | then 10 | rustup component add rustfmt 11 | cargo fmt --version 12 | cargo fmt -- --check || (echo "Please reformat your code with 'cargo fmt' (version $(cargo fmt --version))"; false) 13 | fi 14 | - cargo build --verbose --all 15 | - cargo run --example nested_struct 16 | - cargo run --example simple 17 | - cargo run --example map 18 | os: 19 | - windows 20 | - osx 21 | - linux 22 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde-diff" 3 | version = "0.4.1" 4 | authors = ["Karl Bergström ", "Philip Degarmo "] 5 | readme = "README.md" 6 | exclude = ["examples/*"] 7 | license = "Apache-2.0 OR MIT" 8 | edition = "2018" 9 | description = "A small helper to serialize the diff of two structs of the same type and apply those differences to other structs." 10 | 11 | repository = "https://github.com/amethyst/serde-diff" 12 | 13 | [badges] 14 | maintenance = { status = "experimental" } 15 | 16 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 17 | 18 | [dependencies] 19 | serde-diff-derive = { version = "0.4.0", path = "serde-diff-derive" } 20 | serde = { version = "1", features = [ "derive" ] } 21 | serde_derive = { version = "1", features = ["deserialize_in_place"]} 22 | 23 | [dev-dependencies] 24 | serde_json = "1.0" 25 | bincode = "1.2" 26 | rmp-serde = "0.15.0" 27 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serde-diff 2 | 3 | A small helper that can 4 | 1. Serialize the fields that differ between two values of the same type 5 | 2. Apply previously serialized field differences to other values of the same type. 6 | 7 | The SerdeDiff trait impl can serialize field paths recursively, greatly reducing the amount of data that needs to be serialized when only a small part of a struct/enum has changed. 8 | 9 | [![Build Status][build_img]][build_lnk] [![Crates.io][crates_img]][crates_lnk] [![Docs.rs][doc_img]][doc_lnk] 10 | 11 | [build_img]: https://travis-ci.org/amethyst/serde-diff.svg 12 | [build_lnk]: https://travis-ci.org/amethyst/serde-diff 13 | [crates_img]: https://img.shields.io/crates/v/serde-diff.svg 14 | [crates_lnk]: https://crates.io/crates/serde-diff 15 | [doc_img]: https://docs.rs/serde-diff/badge.svg 16 | [doc_lnk]: https://docs.rs/serde-diff 17 | 18 | ## Usage 19 | On a struct or enum: 20 | ```rust 21 | #[derive(SerdeDiff, Serialize, Deserialize)] 22 | ``` 23 | 24 | Serialize & apply differences for various formats: 25 | 26 | rmp_serde (MessagePack - very small messages) 27 | ```rust 28 | let msgpack_data = rmp_serde::to_vec_named(&Diff::serializable(&old, &new))?; 29 | let mut deserializer = rmp_serde::Deserializer::new(msgpack_data.as_slice()); 30 | Apply::apply(&mut deserializer, &mut target)?; 31 | ``` 32 | 33 | bincode (very fast serialize/deserialize) 34 | ```rust 35 | let bincode_data = bincode::serialize(&Diff::serializable(&old, &new))?; 36 | bincode::config().deserialize_seed(Apply::deserializable(&mut target), &bincode_data)?; 37 | ``` 38 | 39 | serde_json 40 | ```rust 41 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 42 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 43 | Apply::apply(&mut deserializer, &mut target)?; 44 | ``` 45 | 46 | ## Built-in type support 47 | - [x] Primitive types 48 | - [x] std::time::{Duration, SystemTime} 49 | - [x] IP addresses in std 50 | - [x] Vec 51 | - [x] HashMap (thanks @milkey-mouse) 52 | - [x] BTreeMap (thanks @milkey-mouse) 53 | - [x] Fixed-size arrays (thanks @Boscop) 54 | - [x] Tuples (thanks @Boscop) 55 | 56 | # Simple example 57 | 58 | `Cargo.toml` 59 | ```toml 60 | [dependencies] 61 | serde-diff = "0.3" 62 | serde = "1" 63 | serde_json = "1" # all serde formats are supported, serde_json is shown in this example 64 | ``` 65 | `main.rs` 66 | ```rust 67 | use serde_diff::{Apply, Diff, SerdeDiff}; 68 | use serde::{Serialize, Deserialize}; 69 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 70 | struct TestStruct { 71 | a: u32, 72 | b: f64, 73 | } 74 | 75 | fn main() { 76 | let old = TestStruct { 77 | a: 5, 78 | b: 2., 79 | }; 80 | let new = TestStruct { 81 | a: 8, // Differs from old.a, will be serialized 82 | b: 2., 83 | }; 84 | let mut target = TestStruct { 85 | a: 0, 86 | b: 4., 87 | }; 88 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new)).unwrap(); 89 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 90 | Apply::apply(&mut deserializer, &mut target).unwrap(); 91 | 92 | 93 | let result = TestStruct { 94 | a: 8, 95 | b: 4., 96 | }; 97 | assert_eq!(result, target); 98 | } 99 | ``` 100 | 101 | ## Derive macro attributes 102 | Opaque structs: 103 | ```rust 104 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 105 | #[serde_diff(opaque)] // opaque structs are serialized as a unit and fields do not need to implement SerdeDiff 106 | struct DoesNotRecurse { 107 | value: ExternalType, 108 | } 109 | ``` 110 | 111 | Opaque fields: 112 | ```rust 113 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 114 | struct WrapperStruct { 115 | #[serde_diff(opaque)] 116 | value: ExternalType, // opaque fields only need to implement Serialize + Deserialize + PartialEq, 117 | } 118 | ``` 119 | 120 | Skip fields: 121 | ```rust 122 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 123 | struct WrapperStruct { 124 | #[serde_diff(skip)] 125 | value: ExternalType, 126 | } 127 | ``` 128 | 129 | Generics: 130 | ```rust 131 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 132 | struct GenericStruct 133 | where 134 | T: SerdeDiff, 135 | { 136 | a: T, 137 | } 138 | ``` 139 | 140 | Enums: 141 | ```rust 142 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 143 | enum TestEnum { 144 | Structish { x: u32, y: u32 }, 145 | Enumish(i32, i32, i32), 146 | Unitish, 147 | } 148 | ``` 149 | 150 | ## Contribution 151 | 152 | All contributions are assumed to be dual-licensed under MIT/Apache-2. 153 | 154 | ## License 155 | 156 | Distributed under the terms of both the MIT license and the Apache License (Version 2.0). 157 | 158 | See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT). 159 | -------------------------------------------------------------------------------- /examples/enum.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_diff::{Apply, Diff, SerdeDiff}; 3 | 4 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 5 | enum TestEnum { 6 | Structish { x: u32, y: u32 }, 7 | Enumish(i32, i32, i32), 8 | Unitish, 9 | } 10 | 11 | fn main() -> Result<(), Box> { 12 | { 13 | let old = TestEnum::Structish { x: 5, y: 2 }; 14 | let new = TestEnum::Structish { 15 | x: 8, // Differs from old.a, will be serialized 16 | y: 2, 17 | }; 18 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 19 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 20 | let mut target = TestEnum::Structish { x: 0, y: 4 }; 21 | Apply::apply(&mut deserializer, &mut target)?; 22 | 23 | let result = TestEnum::Structish { x: 8, y: 4 }; 24 | assert_eq!(result, target); 25 | } 26 | { 27 | let old = TestEnum::Structish { x: 5, y: 2 }; 28 | let new = TestEnum::Structish { 29 | x: 8, // Differs from old.a, will be serialized 30 | y: 2, 31 | }; 32 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 33 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 34 | let mut target = TestEnum::Enumish(1, 2, 3); 35 | Apply::apply(&mut deserializer, &mut target)?; 36 | /* we can't apply changes from one enum variant 37 | to another, as there may be unfilled fields with no sane defaults, 38 | should there be an error? probably.*/ 39 | let result = TestEnum::Enumish(1, 2, 3); 40 | assert_eq!(result, target); 41 | } 42 | { 43 | let old = TestEnum::Enumish(1, 2, 3); 44 | let new = TestEnum::Enumish(1, 10, 3); 45 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 46 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 47 | let mut target = TestEnum::Enumish(4, 3, 2); 48 | Apply::apply(&mut deserializer, &mut target)?; 49 | let result = TestEnum::Enumish(4, 10, 2); 50 | assert_eq!(result, target); 51 | } 52 | { 53 | let old = TestEnum::Structish { x: 5, y: 2 }; 54 | let new = TestEnum::Unitish; 55 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 56 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 57 | let mut target = TestEnum::Enumish(1, 2, 3); 58 | Apply::apply(&mut deserializer, &mut target)?; 59 | let result = TestEnum::Unitish; 60 | assert_eq!(result, target); 61 | } 62 | Ok(()) 63 | } 64 | -------------------------------------------------------------------------------- /examples/map.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_diff::{Apply, Diff, SerdeDiff}; 3 | use std::collections::HashMap; 4 | 5 | #[derive(SerdeDiff, Serialize, Deserialize, Debug, Default, PartialEq, Clone)] 6 | struct TestStruct { 7 | test: bool, 8 | //#[serde_diff(opaque)] 9 | map: HashMap>, 10 | } 11 | 12 | fn main() -> Result<(), Box> { 13 | let mut empty = TestStruct::default(); 14 | empty.test = true; 15 | 16 | let mut hello_world = TestStruct::default(); 17 | hello_world 18 | .map 19 | .insert("hello".to_string(), vec!["world".to_string()]); 20 | 21 | let mut hi_world = TestStruct::default(); 22 | hi_world 23 | .map 24 | .insert("hi".to_string(), vec!["world".to_string()]); 25 | 26 | let mut hi_world_and_planet = TestStruct::default(); 27 | hi_world_and_planet.map.insert( 28 | "hi".to_string(), 29 | vec!["world".to_string(), "planet".to_string()], 30 | ); 31 | 32 | let mut hi_planet = TestStruct::default(); 33 | hi_planet 34 | .map 35 | .insert("hi".to_string(), vec!["planet".to_string()]); 36 | 37 | let add_hello = serde_json::to_string(&Diff::serializable(&empty, &hello_world))?; 38 | let hello_to_hi = serde_json::to_string(&Diff::serializable(&hello_world, &hi_world))?; 39 | let add_planet = serde_json::to_string(&Diff::serializable(&hi_world, &hi_world_and_planet))?; 40 | let del_world = serde_json::to_string(&Diff::serializable(&hi_world_and_planet, &hi_planet))?; 41 | let no_change = serde_json::to_string(&Diff::serializable(&hi_planet, &hi_planet))?; 42 | 43 | let mut built = TestStruct::default(); 44 | for (diff, after) in &[ 45 | (add_hello, hello_world), 46 | (hello_to_hi, hi_world), 47 | (add_planet, hi_world_and_planet), 48 | (del_world, hi_planet.clone()), 49 | (no_change, hi_planet), 50 | ] { 51 | println!("{}", diff); 52 | 53 | let mut deserializer = serde_json::Deserializer::from_str(&diff); 54 | Apply::apply(&mut deserializer, &mut built)?; 55 | 56 | assert_eq!(after, &built); 57 | } 58 | Ok(()) 59 | } 60 | -------------------------------------------------------------------------------- /examples/nested_struct.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_diff::{opaque_serde_diff, Apply, Diff, SerdeDiff}; 3 | 4 | // Example of implementing diff support for trivial type. Must implement 5 | // Serialize + Deserialize + PartialEq. 6 | #[derive(PartialEq, Serialize, Deserialize, Clone, Debug)] 7 | struct SimpleWrapper(u32); 8 | opaque_serde_diff!(SimpleWrapper); 9 | 10 | // Minimal example of implementing diff support for a struct 11 | #[derive(SerdeDiff, Serialize, Deserialize, Clone, Debug)] 12 | struct MySimpleStruct { 13 | val: u32, 14 | } 15 | 16 | // Example of an "opaque" implementation of SerdeDiff using `#[serde_diff(opaque)]` 17 | #[derive(SerdeDiff, Clone, Serialize, Deserialize, PartialEq, Debug)] 18 | #[serde_diff(opaque)] 19 | struct OpaqueTest(i32); 20 | 21 | // This struct is contained within MyStruct for a more complex example case 22 | #[derive(SerdeDiff, Clone, Serialize, Deserialize, Debug)] 23 | struct MyInnerStruct { 24 | x: f32, 25 | opaque: OpaqueTest, 26 | a_string: String, 27 | string_list: Vec, 28 | string_list2: Vec, 29 | nested_vec: Vec, 30 | } 31 | 32 | // This is a more complex struct containing another complex struct 33 | #[derive(SerdeDiff, Clone, Serialize, Deserialize, Debug)] 34 | struct MyStruct { 35 | a: f32, 36 | b: i32, 37 | s: String, 38 | c: MyInnerStruct, 39 | simple: SimpleWrapper, 40 | } 41 | 42 | fn main() -> Result<(), Box> { 43 | // Create old state 44 | let old = MyStruct { 45 | a: 5.0, 46 | b: 31, 47 | s: "A string".to_string(), 48 | c: MyInnerStruct { 49 | x: 40.0, 50 | opaque: OpaqueTest(9), 51 | a_string: "my string".to_string(), 52 | string_list: vec!["str1".to_string(), "str3".to_string()], 53 | string_list2: vec!["str6".to_string(), "str7".to_string()], 54 | nested_vec: vec![MySimpleStruct { val: 8 }], 55 | }, 56 | simple: SimpleWrapper(10), 57 | }; 58 | 59 | // Create new state 60 | let new = MyStruct { 61 | a: 3.0, 62 | b: 32, 63 | s: "A string".to_string(), 64 | c: MyInnerStruct { 65 | x: 39.0, 66 | opaque: OpaqueTest(4), 67 | a_string: "my other string".to_string(), 68 | string_list: vec!["str1".to_string(), "str2".to_string(), "str3".to_string()], 69 | string_list2: vec!["str6".to_string()], 70 | nested_vec: vec![MySimpleStruct { val: 6 }], 71 | }, 72 | simple: SimpleWrapper(4), 73 | }; 74 | 75 | // Create a diff of the to structures. This just stores a reference to the old/new values 76 | // and doesn't walk any fields yet. The actual diff will be performed when this struct is 77 | // serialized 78 | let diff = Diff::serializable(&old, &new); 79 | 80 | // Serialize into a couple different formats 81 | let json_data = serde_json::to_string(&diff)?; 82 | let bincode_data = bincode::serialize(&diff)?; 83 | // Waiting on a fix to https://github.com/3Hren/msgpack-rust/issues/235 84 | // let msgpack_data = rmp_serde::to_vec_named(&diff)?; 85 | 86 | println!("{}", &json_data); 87 | // Create a struct to which we will apply a diff. This is a mix of old and new state from 88 | // the diff 89 | let target = MyStruct { 90 | a: 5.0, // old, 5.0 -> 3.0 91 | b: 31, // old, 31 -> 32 92 | s: "A string".to_string(), // unchanged 93 | c: MyInnerStruct { 94 | x: 40.0, // old, 40.0 -> 39.0 95 | opaque: OpaqueTest(9), // old, 9 -> 4 96 | a_string: "my string".to_string(), // old, "my string" -> "my other string" 97 | string_list: vec!["str1".to_string(), "str5".to_string()], // does not match old or new, "str2" was added 98 | string_list2: vec!["str6".to_string(), "str7".to_string()], // old, "str7" was removed 99 | nested_vec: vec![MySimpleStruct { val: 3 }], // does not match old or new, 8 -> 6 100 | }, 101 | simple: SimpleWrapper(10), // old, 10 -> 4 102 | }; 103 | 104 | // Demonstrate applying the diff saved as json 105 | { 106 | let mut target = target.clone(); 107 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 108 | Apply::apply(&mut deserializer, &mut target)?; 109 | } 110 | 111 | // Demonstrate applying the diff saved as bincode 112 | { 113 | let mut target = target.clone(); 114 | bincode::config().deserialize_seed(Apply::deserializable(&mut target), &bincode_data)?; 115 | 116 | println!("diff {:#?} and {:#?}", old, new); 117 | println!("result {:#?}", target); 118 | } 119 | 120 | // Demonstrate applying the diff saved as msgpack 121 | // Waiting on a fix to https://github.com/3Hren/msgpack-rust/issues/235 122 | // { 123 | // let mut target = target.clone(); 124 | // let mut deserializer = rmp_serde::Deserializer::new(msgpack_data.as_slice()); 125 | // Apply::apply(&mut deserializer, &mut target)?; 126 | 127 | // println!("diff {:#?} and {:#?}", old, new); 128 | // println!("result {:#?}", target); 129 | // } 130 | 131 | println!( 132 | "bincode size {} json size {} msgpack size {}", 133 | bincode_data.len(), 134 | json_data.len(), 135 | 0, 136 | // msgpack_data.len(), 137 | ); 138 | Ok(()) 139 | } 140 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_diff::{Apply, Diff, SerdeDiff}; 3 | 4 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 5 | struct TestStruct { 6 | a: u32, 7 | b: f64, 8 | } 9 | 10 | fn main() -> Result<(), Box> { 11 | let old = TestStruct { a: 5, b: 2. }; 12 | let new = TestStruct { 13 | a: 8, // Differs from old.a, will be serialized 14 | b: 2., 15 | }; 16 | let mut target = TestStruct { a: 0, b: 4. }; 17 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 18 | println!("{}", json_data); 19 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 20 | Apply::apply(&mut deserializer, &mut target)?; 21 | 22 | let result = TestStruct { a: 8, b: 4. }; 23 | assert_eq!(result, target); 24 | Ok(()) 25 | } 26 | -------------------------------------------------------------------------------- /examples/struct.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use serde_diff::{Apply, Diff, SerdeDiff}; 3 | 4 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 5 | struct TestStruct { 6 | a: u32, 7 | b: f64, 8 | } 9 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 10 | struct Test2Struct(u32, u32, u32); 11 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 12 | struct Test3Struct; 13 | 14 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug)] 15 | struct Test4Struct 16 | where 17 | T: SerdeDiff, 18 | { 19 | a: T, 20 | } 21 | 22 | fn main() -> Result<(), Box> { 23 | { 24 | let old = TestStruct { a: 5, b: 2. }; 25 | let new = TestStruct { 26 | a: 8, // Differs from old.a, will be serialized 27 | b: 2., 28 | }; 29 | let mut target = TestStruct { a: 0, b: 4. }; 30 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 31 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 32 | Apply::apply(&mut deserializer, &mut target)?; 33 | 34 | let result = TestStruct { a: 8, b: 4. }; 35 | assert_eq!(result, target); 36 | } 37 | { 38 | let old = Test2Struct(1, 2, 3); 39 | let new = Test2Struct( 40 | 5, // Differs from old.0, will be serialized 41 | 2, 3, 42 | ); 43 | let mut target = Test2Struct(4, 5, 6); 44 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 45 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 46 | Apply::apply(&mut deserializer, &mut target)?; 47 | 48 | let result = Test2Struct(5, 5, 6); 49 | assert_eq!(result, target); 50 | } 51 | { 52 | let old = Test3Struct; 53 | let new = Test3Struct; 54 | let mut target = Test3Struct; 55 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 56 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 57 | Apply::apply(&mut deserializer, &mut target)?; 58 | 59 | let result = Test3Struct; 60 | assert_eq!(result, target); 61 | } 62 | { 63 | let old = Test4Struct { a: 5 }; 64 | let new = Test4Struct { a: 7 }; 65 | let mut target = Test4Struct { a: 10 }; 66 | let json_data = serde_json::to_string(&Diff::serializable(&old, &new))?; 67 | let mut deserializer = serde_json::Deserializer::from_str(&json_data); 68 | Apply::apply(&mut deserializer, &mut target)?; 69 | 70 | let result = Test4Struct { a: 7 }; 71 | assert_eq!(result, target); 72 | } 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /serde-diff-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde-diff-derive" 3 | version = "0.4.0" 4 | authors = ["Karl Bergström ", "Philip Degarmo "] 5 | readme = "README.md" 6 | license = "Apache-2.0 OR MIT" 7 | edition = "2018" 8 | description = "Derives serde_diff::SerdeDiff" 9 | 10 | repository = "https://github.com/amethyst/serde-diff" 11 | 12 | [badges] 13 | maintenance = { status = "experimental" } 14 | 15 | [lib] 16 | name="serde_diff_derive" 17 | proc-macro = true 18 | 19 | [dependencies] 20 | proc-macro2 = "1.0" 21 | quote = "1.0" 22 | syn = { version = "1.0", features = ["parsing"] } 23 | darling = "0.10" 24 | 25 | [dev-dependencies] 26 | serde = "1" 27 | serde-diff = { path = ".." } -------------------------------------------------------------------------------- /serde-diff-derive/README.md: -------------------------------------------------------------------------------- 1 | # serde-diff-derive 2 | Derives the `serde_diff::SerdeDiff` trait. 3 | 4 | # Usage 5 | ``` 6 | #[derive(SerdeDiff, Serialize, Deserialize)] 7 | struct MyStruct { ... } 8 | ``` -------------------------------------------------------------------------------- /serde-diff-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | mod serde_diff; 4 | 5 | /// # Examples 6 | /// 7 | /// Minimal example of implementing diff support for a struct 8 | /// ```rust 9 | /// use serde_diff::SerdeDiff; 10 | /// use serde::{Serialize, Deserialize}; 11 | /// #[derive(SerdeDiff)] 12 | /// struct MySimpleStruct { 13 | /// val: u32, 14 | /// } 15 | /// ``` 16 | /// 17 | /// Example of an opaque (non-recursive diff) implementation of SerdeDiff using `#[serde_diff(opaque)]` on the struct. 18 | /// Field types are not required to implement SerdeDiff in this case, only Serialize + Deserialize + PartialEq. 19 | /// ```rust 20 | /// use serde_diff::SerdeDiff; 21 | /// use serde::{Serialize, Deserialize}; 22 | /// #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 23 | /// #[serde_diff(opaque)] 24 | /// struct OpaqueTest(i32); 25 | /// ``` 26 | /// 27 | /// Example of a struct with an opaque field using `#[serde_diff(opaque)]` on a field. 28 | /// ```rust 29 | /// use serde_diff::SerdeDiff; 30 | /// use serde::{Serialize, Deserialize}; 31 | /// #[derive(SerdeDiff)] 32 | /// struct MyInnerStruct { 33 | /// #[serde_diff(opaque)] 34 | /// heap: std::collections::HashSet, 35 | /// } 36 | /// ``` 37 | /// 38 | /// Example of diffing a target struct `MySimpleStruct` that is being used for serialization instead 39 | /// of the struct `MyComplexStruct` itself. Useful for cases where derived data is present at 40 | /// runtime, but not wanted in the serialized form. 41 | /// ```rust 42 | /// use serde_diff::SerdeDiff; 43 | /// use serde::{Serialize, Deserialize}; 44 | /// #[derive(SerdeDiff, Serialize, Deserialize, Clone)] 45 | /// #[serde(from = "MySimpleStruct", into = "MySimpleStruct")] 46 | /// #[serde_diff(target = "MySimpleStruct")] 47 | /// struct MyComplexStruct { 48 | /// val: u32, 49 | /// derived_val: String, 50 | /// } 51 | /// 52 | /// #[derive(SerdeDiff, Serialize, Deserialize, Default)] 53 | /// #[serde(rename = "MyComplexStruct", default)] 54 | /// struct MySimpleStruct { 55 | /// val: u32, 56 | /// } 57 | /// 58 | /// impl From for MyComplexStruct { 59 | /// fn from(my_simple_struct: MySimpleStruct) -> Self { 60 | /// MyComplexStruct { 61 | /// val: my_simple_struct.val, 62 | /// derived_val: my_simple_struct.val.to_string(), 63 | /// } 64 | /// } 65 | /// } 66 | /// 67 | /// impl Into for MyComplexStruct { 68 | /// fn into(self) -> MySimpleStruct { 69 | /// MySimpleStruct { 70 | /// val: self.val, 71 | /// } 72 | /// } 73 | /// } 74 | /// ``` 75 | #[proc_macro_derive(SerdeDiff, attributes(serde_diff))] 76 | pub fn serde_diff_macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 77 | serde_diff::macro_derive(input) 78 | } 79 | -------------------------------------------------------------------------------- /serde-diff-derive/src/serde_diff/args.rs: -------------------------------------------------------------------------------- 1 | use darling::{FromDeriveInput, FromField}; 2 | 3 | /// Metadata from the struct's type annotation 4 | #[derive(Debug, FromDeriveInput)] 5 | #[darling(attributes(serde_diff))] 6 | pub struct SerdeDiffStructArgs { 7 | /// Name of the struct 8 | pub ident: syn::Ident, 9 | /// Whether the struct is opaque or not 10 | #[darling(default)] 11 | pub opaque: bool, 12 | /// If specified, the struct we will convert to before performing diff operations 13 | #[darling(default)] 14 | pub target: Option, 15 | 16 | pub generics: syn::Generics, 17 | } 18 | 19 | /// Metadata from the struct's field annotations 20 | #[derive(Debug, FromField, Clone)] 21 | #[darling(attributes(serde_diff))] 22 | pub struct SerdeDiffFieldArgs { 23 | /// Name of the field 24 | ident: Option, 25 | 26 | /// Type of the field 27 | ty: syn::Type, 28 | 29 | /// If true, this field should be ignored 30 | #[darling(default)] 31 | skip: bool, 32 | 33 | /// If true, simple diff should be generated inline 34 | #[darling(default)] 35 | opaque: bool, 36 | } 37 | 38 | impl SerdeDiffFieldArgs { 39 | /// Name of the field 40 | pub fn ident(&self) -> &Option { 41 | &self.ident 42 | } 43 | 44 | /// Type of the field 45 | pub fn ty(&self) -> &syn::Type { 46 | &self.ty 47 | } 48 | 49 | /// If true, simple diff should be generated inline 50 | pub fn skip(&self) -> bool { 51 | self.skip 52 | } 53 | 54 | /// If true, this field should be ignored 55 | 56 | pub fn opaque(&self) -> bool { 57 | self.opaque 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /serde-diff-derive/src/serde_diff/mod.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | mod args; 4 | 5 | use quote::{quote, format_ident}; 6 | 7 | /// Reads in all tokens for the struct having the 8 | pub fn macro_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 9 | use darling::FromDeriveInput; 10 | use syn::Data; 11 | 12 | // Parse the struct 13 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 14 | let struct_args = args::SerdeDiffStructArgs::from_derive_input(&input).unwrap(); 15 | let target_type = if let Some(ref target) = struct_args.target { 16 | let span = proc_macro2::Span::call_site(); 17 | let target_type_result = parse_string_to_type(target.to_owned(), span); 18 | Some(target_type_result.unwrap()) // is there something useful we could do with this error? 19 | } else { 20 | None 21 | }; 22 | match input.data { 23 | Data::Struct(..) | Data::Enum(..) => { 24 | if struct_args.opaque { 25 | generate_opaque(&input, struct_args) 26 | } else { 27 | // Go ahead and generate the code 28 | match generate(&input, struct_args, target_type) { 29 | Ok(v) => v, 30 | Err(v) => v, 31 | } 32 | } 33 | } 34 | _ => unimplemented!() 35 | } 36 | } 37 | 38 | /// Called per field to parse and verify it 39 | fn parse_field(f: &syn::Field) -> Result { 40 | use darling::FromField; 41 | let field_args = args::SerdeDiffFieldArgs::from_field(&f)?; 42 | Ok(ParsedField { field_args }) 43 | } 44 | 45 | fn parse_fields(input: &syn::Fields) -> Vec> { 46 | use syn::Fields; 47 | match input { 48 | Fields::Named(ref fields) => fields.named.iter().map(|f| parse_field(&f)).collect(), 49 | Fields::Unnamed(ref fields) => fields.unnamed.iter().map(|f| parse_field(&f)).collect(), 50 | Fields::Unit => vec![], 51 | } 52 | } 53 | 54 | /// Parsed metadata for a field 55 | #[derive(Debug)] 56 | struct ParsedField { 57 | field_args: args::SerdeDiffFieldArgs, 58 | } 59 | 60 | fn generate_fields_diff( 61 | parsed_fields: &[ParsedField], 62 | matching : bool, 63 | ) -> proc_macro2::TokenStream { 64 | // This will hold a bit of code per-field that call diff on that field 65 | let mut diff_fn_field_handlers = vec![]; 66 | for (field_idx, pf) in parsed_fields.iter().enumerate() { 67 | // Skip fields marked as #[serde_diff(skip)] 68 | if pf.field_args.skip() { 69 | continue; 70 | } 71 | 72 | let ident = pf.field_args.ident().clone(); 73 | let ident_as_str = quote!(#ident).to_string(); 74 | let ty = pf.field_args.ty(); 75 | let field_idx = field_idx as u16; 76 | let left = format_ident!("l{}", field_idx); 77 | let right = format_ident!("r{}", field_idx); 78 | 79 | let push = if let Some(_) = ident { 80 | quote!{ctx.push_field(#ident_as_str);} 81 | } else { 82 | quote!{ctx.push_field_index(#field_idx);} 83 | }; 84 | 85 | if pf.field_args.opaque() || !matching { 86 | let cmp = if matching { 87 | quote! { #left != #right } 88 | } else { 89 | quote! {true} 90 | }; 91 | diff_fn_field_handlers.push(quote! { 92 | { 93 | #push 94 | if #cmp { 95 | ctx.save_value(&#right)?; 96 | __changed__ |= true; 97 | } 98 | ctx.pop_path_element()?; 99 | } 100 | }); 101 | } else { 102 | diff_fn_field_handlers.push(quote! { 103 | { 104 | { 105 | #push 106 | __changed__ |= <#ty as serde_diff::SerdeDiff>::diff(&#left, ctx, &#right)?; 107 | ctx.pop_path_element()?; 108 | } 109 | } 110 | }); 111 | } 112 | } 113 | quote! { 114 | #(#diff_fn_field_handlers)* 115 | } 116 | } 117 | 118 | 119 | fn enum_fields(fields : &syn::Fields, mutable: bool) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { 120 | use syn::Fields; 121 | let field_match = |f: &syn::Field, name, idx| { 122 | let name = format_ident!("{}{}", name, idx); 123 | let mut_tok = if mutable { 124 | Some(quote!(ref mut)) 125 | } else { 126 | None 127 | }; 128 | if let Some(n) = &f.ident { 129 | quote! { #n : #mut_tok #name } 130 | } else { 131 | quote! {#mut_tok #name} 132 | } 133 | }; 134 | let fields_match = |fields: &syn::Fields, prefix| { 135 | match fields { 136 | Fields::Named(n) => { 137 | n.named.iter().enumerate().map(|(i, f)| field_match(f, prefix, i)).collect() 138 | }, 139 | Fields::Unnamed(n) => { 140 | n.unnamed.iter().enumerate().map(|(i, f)| field_match(f, prefix, i)).collect() 141 | }, 142 | Fields::Unit => vec![] 143 | } 144 | }; 145 | let (left, right) = (fields_match(fields, "l"), fields_match(fields, "r")); 146 | let (left, right) = match fields { 147 | Fields::Named(_) => (quote!{{#(#left),*}}, quote!{{#(#right),*}}), 148 | Fields::Unnamed(_) => (quote!{(#(#left),*)}, quote!{(#(#right),*)}), 149 | Fields::Unit => (quote!{}, quote!{}), 150 | }; 151 | (left, right) 152 | } 153 | 154 | fn ok_fields(fields : &syn::Fields) -> Result, proc_macro::TokenStream> { 155 | let parsed_fields = parse_fields(fields); 156 | // Check all parsed fields for any errors that may have occurred 157 | let mut ok_fields: Vec = vec![]; 158 | let mut errors = vec![]; 159 | for pf in parsed_fields { 160 | match pf { 161 | Ok(value) => ok_fields.push(value), 162 | Err(e) => errors.push(e), 163 | } 164 | } 165 | // If any error occurred, return them all here 166 | if !errors.is_empty() { 167 | Err(proc_macro::TokenStream::from(darling::Error::multiple(errors).write_errors())) 168 | } else { 169 | Ok(ok_fields) 170 | } 171 | } 172 | 173 | fn generate_arms(name: &syn::Ident, variant: Option<&syn::Ident>, fields: &syn::Fields, matching: bool) 174 | -> Result<(Vec, Vec), 175 | proc_macro2::TokenStream> 176 | { 177 | let mut diff_match_arms = vec![]; 178 | let mut apply_match_arms = vec![]; 179 | let parsed_fields = ok_fields(&fields)?; 180 | let diffs = generate_fields_diff( 181 | &parsed_fields, 182 | matching, 183 | ); 184 | let (left, right) = enum_fields(&fields, false); 185 | let variant_specifier = if let Some(id) = variant { 186 | quote!{ :: #id} 187 | } else { 188 | quote!{} 189 | }; 190 | 191 | let variant_as_str = variant.map(|i| i.to_string()); 192 | let push_variant = variant.map(|_| quote!{ctx.push_variant(#variant_as_str);}); 193 | let pop_variant = variant.map(|_| quote!{ctx.pop_path_element()?;}); 194 | 195 | let left = if matching { 196 | quote! { #name #variant_specifier #left } 197 | } else { 198 | quote! {_} 199 | }; 200 | if matching { 201 | diff_match_arms.push( 202 | quote!{ 203 | (#left, #name #variant_specifier #right) => { 204 | #push_variant 205 | #diffs 206 | #pop_variant 207 | } 208 | } 209 | ); 210 | } else { 211 | diff_match_arms.push( 212 | quote!{ 213 | (#left, #name #variant_specifier #right) => { 214 | ctx.push_full_variant(); 215 | ctx.save_value(other)?; 216 | ctx.pop_path_element()?; 217 | } 218 | } 219 | ); 220 | } 221 | 222 | if matching { 223 | let (left, _right) = enum_fields(fields, true); 224 | let mut apply_fn_field_handlers = vec![]; 225 | for (field_idx, pf) in parsed_fields.iter().enumerate() { 226 | // Skip fields marked as #[serde_diff(skip)] 227 | if pf.field_args.skip() { 228 | continue; 229 | } 230 | 231 | let ident = pf.field_args.ident().clone(); 232 | let ty = pf.field_args.ty(); 233 | let field_idx = field_idx as u16; 234 | 235 | let lhs = format_ident!("l{}", field_idx); 236 | if pf.field_args.opaque() { 237 | apply_fn_field_handlers.push(quote!( 238 | serde_diff::DiffPathElementValue::FieldIndex(#field_idx) => 239 | __changed__ |= ctx.read_value(seq, #lhs)?, 240 | )); 241 | if let Some(ident_as_str) = ident.map(|s| s.to_string()) { 242 | apply_fn_field_handlers.push(quote!( 243 | serde_diff::DiffPathElementValue::Field(field) if field == #ident_as_str => 244 | __changed__ |= ctx.read_value(seq, #lhs)?, 245 | )); 246 | } 247 | } else { 248 | apply_fn_field_handlers.push(quote!( 249 | serde_diff::DiffPathElementValue::FieldIndex(#field_idx) => 250 | __changed__ |= <#ty as serde_diff::SerdeDiff>::apply(#lhs, seq, ctx)?, 251 | )); 252 | if let Some(ident_as_str) = ident.map(|s| s.to_string()) { 253 | apply_fn_field_handlers.push(quote!( 254 | serde_diff::DiffPathElementValue::Field(field) if field == #ident_as_str => 255 | __changed__ |= <#ty as serde_diff::SerdeDiff>::apply(#lhs, seq, ctx)?, 256 | )); 257 | } 258 | } 259 | } 260 | 261 | if let Some(_) = variant { 262 | apply_match_arms.push(quote!{ 263 | ( &mut #name #variant_specifier #left, Some(serde_diff::DiffPathElementValue::EnumVariant(variant))) if variant == #variant_as_str => { 264 | while let Some(element) = ctx.next_path_element(seq)? { 265 | match element { 266 | #(#apply_fn_field_handlers)* 267 | _ => ctx.skip_value(seq)? 268 | } 269 | } 270 | } 271 | }); 272 | } else { 273 | apply_match_arms.push(quote!{ 274 | ( &mut #name #variant_specifier #left) => { 275 | while let Some(element) = ctx.next_path_element(seq)? { 276 | match element { 277 | #(#apply_fn_field_handlers)* 278 | _ => ctx.skip_value(seq)? 279 | } 280 | } 281 | } 282 | }); 283 | } 284 | } 285 | 286 | Ok((diff_match_arms, apply_match_arms)) 287 | } 288 | 289 | fn generate( 290 | input: &syn::DeriveInput, 291 | struct_args: args::SerdeDiffStructArgs, 292 | target_type: Option, 293 | ) -> Result { 294 | 295 | use syn::Data; 296 | let mut diff_match_arms = vec![]; 297 | let mut apply_match_arms = vec![]; 298 | 299 | let has_variants = match &input.data { 300 | Data::Enum(e) => { 301 | for matching in &[true, false] { 302 | for v in &e.variants { 303 | let (diff, apply) = generate_arms(&struct_args.ident, Some(&v.ident), &v.fields, *matching)?; 304 | diff_match_arms.extend(diff); 305 | apply_match_arms.extend(apply); 306 | } 307 | } 308 | true 309 | } 310 | Data::Struct(s) => { 311 | let matching = true; 312 | let (diff, apply) = generate_arms(&struct_args.ident, None, &s.fields, matching)?; 313 | diff_match_arms.extend(diff); 314 | apply_match_arms.extend(apply); 315 | false 316 | } 317 | _ => {unreachable!("Unhandled Type in Enum")}, 318 | }; 319 | 320 | // Generate the SerdeDiff::diff function for the type 321 | let diff_fn = if let Some(ref ty) = target_type { 322 | quote! { 323 | fn diff<'a, S: serde_diff::_serde::ser::SerializeSeq>(&self, ctx: &mut serde_diff::DiffContext<'a, S>, other: &Self) -> Result { 324 | std::convert::Into::<#ty>::into(std::clone::Clone::clone(self)) 325 | .diff(ctx, &std::convert::Into::<#ty>::into(std::clone::Clone::clone(other))) 326 | } 327 | } 328 | } else { 329 | quote! { 330 | fn diff<'a, S: serde_diff::_serde::ser::SerializeSeq>(&self, ctx: &mut serde_diff::DiffContext<'a, S>, other: &Self) -> Result { 331 | let mut __changed__ = false; 332 | match (self, other) { 333 | #(#diff_match_arms)* 334 | } 335 | Ok(__changed__) 336 | } 337 | } 338 | }; 339 | 340 | 341 | // Generate the SerdeDiff::apply function for the type 342 | //TODO: Consider using something like the phf crate to avoid a string compare across field names, 343 | // or consider having the user manually tag their data with a number similar to protobuf 344 | let apply_fn = if let Some(ref ty) = target_type { 345 | quote! { 346 | fn apply<'de, A>( 347 | &mut self, 348 | seq: &mut A, 349 | ctx: &mut serde_diff::ApplyContext, 350 | ) -> Result>::Error> 351 | where 352 | A: serde_diff::_serde::de::SeqAccess<'de>, { 353 | let mut converted = std::convert::Into::<#ty>::into(std::clone::Clone::clone(self)); 354 | let result = converted.apply(seq, ctx); 355 | *self = std::convert::From::<#ty>::from(converted); 356 | result 357 | } 358 | } 359 | } else { 360 | if has_variants { 361 | quote! { 362 | fn apply<'de, A>( 363 | &mut self, 364 | seq: &mut A, 365 | ctx: &mut serde_diff::ApplyContext, 366 | ) -> Result>::Error> 367 | where 368 | A: serde_diff::_serde::de::SeqAccess<'de>, { 369 | let mut __changed__ = false; 370 | match (self, ctx.next_path_element(seq)?) { 371 | (this, Some(serde_diff::DiffPathElementValue::FullEnumVariant)) => { 372 | ctx.read_value(seq, this)?; 373 | __changed__ = true; 374 | } 375 | #(#apply_match_arms)* 376 | _ => ctx.skip_value(seq)?, 377 | } 378 | Ok(__changed__) 379 | } 380 | } 381 | } else { 382 | quote! { 383 | fn apply<'de, A>( 384 | &mut self, 385 | seq: &mut A, 386 | ctx: &mut serde_diff::ApplyContext, 387 | ) -> Result>::Error> 388 | where 389 | A: serde_diff::_serde::de::SeqAccess<'de>, { 390 | let mut __changed__ = false; 391 | match (self) { 392 | #(#apply_match_arms)* 393 | _ => ctx.skip_value(seq)?, 394 | } 395 | Ok(__changed__) 396 | } 397 | } 398 | } 399 | }; 400 | 401 | // Generate the impl block with the diff and apply functions within it 402 | let struct_name = &struct_args.ident; 403 | let generics = &struct_args.generics.params; 404 | let where_clause = &struct_args.generics.where_clause; 405 | let diff_impl = quote! { 406 | impl <#generics> serde_diff::SerdeDiff for #struct_name < #generics> #where_clause { 407 | #diff_fn 408 | #apply_fn 409 | } 410 | }; 411 | return Ok(proc_macro::TokenStream::from(quote! { 412 | #diff_impl 413 | })); 414 | } 415 | 416 | fn generate_opaque( 417 | _input: &syn::DeriveInput, 418 | struct_args: args::SerdeDiffStructArgs, 419 | ) -> proc_macro::TokenStream { 420 | let struct_name = &struct_args.ident; 421 | let diff_impl = quote! { 422 | impl serde_diff::SerdeDiff for #struct_name { 423 | fn diff<'a, S: serde_diff::_serde::ser::SerializeSeq>(&self, ctx: &mut serde_diff::DiffContext<'a, S>, other: &Self) -> Result { 424 | if self != other { 425 | ctx.save_value(other)?; 426 | Ok(true) 427 | } else { 428 | Ok(false) 429 | } 430 | } 431 | fn apply<'de, A>( 432 | &mut self, 433 | seq: &mut A, 434 | ctx: &mut serde_diff::ApplyContext, 435 | ) -> Result>::Error> 436 | where 437 | A: serde_diff::_serde::de::SeqAccess<'de>, { 438 | ctx.read_value(seq, self) 439 | } 440 | } 441 | }; 442 | 443 | return proc_macro::TokenStream::from(quote! { 444 | #diff_impl 445 | }); 446 | } 447 | 448 | // Adapted from serde's internal `parse_lit_into_ty` function (with the chain of helper functions directly cargo culted over) 449 | fn parse_string_to_type(s: String, span: proc_macro2::Span) -> Result { 450 | let lit = syn::LitStr::new(&s, span); 451 | let tokens = spanned_tokens(&lit)?; 452 | syn::parse2(tokens) 453 | } 454 | 455 | fn spanned_tokens(s: &syn::LitStr) -> syn::parse::Result { 456 | let stream = syn::parse_str(&s.value())?; 457 | Ok(respan_token_stream(stream, s.span())) 458 | } 459 | 460 | fn respan_token_stream(stream: proc_macro2::TokenStream, span: proc_macro2::Span) -> proc_macro2::TokenStream { 461 | stream 462 | .into_iter() 463 | .map(|token| respan_token_tree(token, span)) 464 | .collect() 465 | } 466 | 467 | fn respan_token_tree(mut token: proc_macro2::TokenTree, span: proc_macro2::Span) -> proc_macro2::TokenTree { 468 | if let proc_macro2::TokenTree::Group(g) = &mut token { 469 | *g = proc_macro2::Group::new(g.delimiter(), respan_token_stream(g.stream(), span)); 470 | } 471 | token.set_span(span); 472 | token 473 | } 474 | 475 | -------------------------------------------------------------------------------- /src/apply.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | difference::{ 3 | DeserWrapper, DiffCommandDeserWrapper, DiffCommandIgnoreValue, DiffCommandValue, 4 | DiffPathElementValue, 5 | }, 6 | Config, SerdeDiff, 7 | }; 8 | use serde::{de, Deserialize}; 9 | 10 | /// A deserializable structure that will apply a sequence of diff commands to the target 11 | /// 12 | /// # Examples 13 | /// 14 | /// ```rust 15 | /// use serde_diff::{SerdeDiff, Diff, Apply}; 16 | /// use serde::{Serialize, Deserialize}; 17 | /// #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 18 | /// struct Test { 19 | /// a: i32, 20 | /// } 21 | /// let diff = Diff::serializable(&Test { a: 3 }, &Test { a: 5 }); 22 | /// let msgpack_data = rmp_serde::to_vec_named(&diff).expect("failed to serialize diff"); 23 | /// let mut deserializer = rmp_serde::Deserializer::new(msgpack_data.as_slice()); 24 | /// let mut target = Test { a: 4 }; 25 | /// Apply::apply(&mut deserializer, &mut target).expect("failed when deserializing diff"); 26 | /// ``` 27 | pub struct Apply<'a, T: SerdeDiff> { 28 | pub(crate) target: &'a mut T, 29 | } 30 | 31 | impl<'a, 'de, T: SerdeDiff> Apply<'a, T> { 32 | /// Create a deserializable apply, where the given target will be changed when the resulting 33 | /// Apply struct is deserialized 34 | pub fn deserializable(target: &'a mut T) -> Self { 35 | Config::default().deserializable_apply(target) 36 | } 37 | 38 | /// Applies a sequence of diff commands to the target, as read by the deserializer 39 | pub fn apply( 40 | deserializer: D, 41 | target: &mut T, 42 | ) -> Result<(), >::Error> 43 | where 44 | D: de::Deserializer<'de>, 45 | { 46 | Config::default().apply(deserializer, target) 47 | } 48 | } 49 | 50 | impl<'a, 'de, T: SerdeDiff> de::DeserializeSeed<'de> for Apply<'a, T> { 51 | type Value = (); 52 | fn deserialize(self, deserializer: D) -> Result 53 | where 54 | D: de::Deserializer<'de>, 55 | { 56 | deserializer.deserialize_seq(self) 57 | } 58 | } 59 | 60 | impl<'a, 'de, T: SerdeDiff> de::Visitor<'de> for Apply<'a, T> { 61 | type Value = (); 62 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 63 | write!(formatter, "a sequence containing DiffCommands") 64 | } 65 | 66 | fn visit_seq(self, mut seq: A) -> Result>::Error> 67 | where 68 | A: de::SeqAccess<'de>, 69 | { 70 | let mut ctx = ApplyContext {}; 71 | self.target.apply(&mut seq, &mut ctx)?; 72 | Ok(()) 73 | } 74 | } 75 | 76 | /// Used during an apply operation for transient data used during the apply 77 | #[doc(hidden)] 78 | pub struct ApplyContext {} 79 | 80 | impl ApplyContext { 81 | /// Returns the next element if it is a path. If it is a Value or Exit, it returns None. 82 | pub fn next_path_element<'de, A>( 83 | &mut self, 84 | seq: &mut A, 85 | ) -> Result>, >::Error> 86 | where 87 | A: de::SeqAccess<'de>, 88 | { 89 | use DiffCommandValue::*; 90 | let element = match seq.next_element_seed(DiffCommandIgnoreValue {})? { 91 | Some(Enter(element)) => Ok(Some(element)), 92 | Some(AddKey(_)) | Some(EnterKey(_)) | Some(RemoveKey(_)) => { 93 | //self.skip_value(seq); 94 | Ok(None) 95 | } 96 | Some(Value(_)) | Some(Remove(_)) => panic!("unexpected DiffCommand Value or Remove"), 97 | Some(Exit) | Some(Nothing) | Some(DeserializedValue) | None => Ok(None), 98 | }; 99 | element 100 | } 101 | /// To be called after next_path_element returns a path, but the path is not recognized. 102 | pub fn skip_value<'de, A>( 103 | &mut self, 104 | seq: &mut A, 105 | ) -> Result<(), >::Error> 106 | where 107 | A: de::SeqAccess<'de>, 108 | { 109 | self.skip_value_internal(seq, 1) 110 | } 111 | fn skip_value_internal<'de, A>( 112 | &mut self, 113 | seq: &mut A, 114 | mut depth: i32, 115 | ) -> Result<(), >::Error> 116 | where 117 | A: de::SeqAccess<'de>, 118 | { 119 | // this tries to skip the value without knowing the type - not possible for some formats.. 120 | while let Some(cmd) = seq.next_element_seed(DiffCommandIgnoreValue {})? { 121 | match cmd { 122 | DiffCommandValue::Enter(_) 123 | | DiffCommandValue::AddKey(_) 124 | | DiffCommandValue::EnterKey(_) => depth += 1, 125 | DiffCommandValue::Exit => depth -= 1, 126 | DiffCommandValue::Value(_) | DiffCommandValue::Remove(_) => depth -= 1, // ignore value, but reduce depth, as it is an implicit Exit 127 | DiffCommandValue::RemoveKey(_) => {} 128 | DiffCommandValue::Nothing | DiffCommandValue::DeserializedValue => { 129 | panic!("should never serialize cmd Nothing or DeserializedValue") 130 | } 131 | } 132 | if depth == 0 { 133 | break; 134 | } 135 | } 136 | if depth != 0 { 137 | panic!("mismatched DiffCommand::Enter/Exit ") 138 | } 139 | Ok(()) 140 | } 141 | /// Attempts to deserialize a value 142 | pub fn read_value<'de, A, T: for<'c> Deserialize<'c>>( 143 | &mut self, 144 | seq: &mut A, 145 | val: &mut T, 146 | ) -> Result>::Error> 147 | where 148 | A: de::SeqAccess<'de>, 149 | { 150 | // The visitor for DiffCommandDeserWrapper handles enum cases and returns 151 | // a command if the next element was not a Value 152 | let cmd = seq.next_element_seed::>(DiffCommandDeserWrapper { 153 | val_wrapper: DeserWrapper { val }, 154 | })?; 155 | match cmd { 156 | Some(DiffCommandValue::DeserializedValue) => return Ok(true), 157 | Some(DiffCommandValue::Enter(_)) => { 158 | self.skip_value_internal(seq, 1)?; 159 | } 160 | Some(DiffCommandValue::Exit) => panic!("unexpected Exit command"), 161 | _ => {} 162 | } 163 | 164 | Ok(false) 165 | } 166 | /// Returns the next command in the stream. Make sure you know what you're doing! 167 | pub fn read_next_command<'de, A, T: for<'c> Deserialize<'c>>( 168 | &mut self, 169 | seq: &mut A, 170 | ) -> Result>, >::Error> 171 | where 172 | A: de::SeqAccess<'de>, 173 | { 174 | // The visitor for DiffCommandDeserWrapper handles enum cases and returns 175 | // a command if the next element was not a Value 176 | let cmd = seq.next_element::>()?; 177 | Ok(match cmd { 178 | cmd @ Some(DiffCommandValue::Remove(_)) 179 | | cmd @ Some(DiffCommandValue::Value(_)) 180 | | cmd @ Some(DiffCommandValue::Enter(_)) 181 | | cmd @ Some(DiffCommandValue::AddKey(_)) 182 | | cmd @ Some(DiffCommandValue::EnterKey(_)) 183 | | cmd @ Some(DiffCommandValue::RemoveKey(_)) 184 | | cmd @ Some(DiffCommandValue::Exit) => cmd, 185 | _ => None, 186 | }) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use crate::{Apply, Diff, FieldPathMode, SerdeDiff}; 2 | use serde::{de, Serialize, Serializer}; 3 | use std::cell::Cell; 4 | 5 | /// Configures creation of `Apply` and `Diff` 6 | /// 7 | /// # Examples 8 | /// 9 | /// ```rust 10 | /// use serde_diff::{SerdeDiff, Config, FieldPathMode}; 11 | /// use serde::{Serialize, Deserialize}; 12 | /// #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 13 | /// struct Test { 14 | /// a: i32, 15 | /// } 16 | /// let diff = Config::new() 17 | /// .with_field_path_mode(FieldPathMode::Index) 18 | /// .serializable_diff(&Test { a: 3 }, &Test { a: 5 }); 19 | /// ``` 20 | pub struct Config { 21 | field_path_mode: FieldPathMode, 22 | } 23 | 24 | impl Default for Config { 25 | fn default() -> Self { 26 | Self { 27 | field_path_mode: FieldPathMode::Name, 28 | } 29 | } 30 | } 31 | 32 | impl Config { 33 | /// Creates a `Config` with default values 34 | pub fn new() -> Self { 35 | ::default() 36 | } 37 | 38 | /// Sets the `FieldPathMode` to use when serializing a Diff 39 | pub fn with_field_path_mode(mut self, mode: FieldPathMode) -> Self { 40 | self.field_path_mode = mode; 41 | self 42 | } 43 | 44 | /// Create a serializable Diff, which when serialized will write the differences between the old 45 | /// and new value into the serializer in the form of a sequence of diff commands 46 | pub fn serializable_diff<'a, 'b, T: SerdeDiff + 'a + 'b>( 47 | self, 48 | old: &'a T, 49 | new: &'b T, 50 | ) -> Diff<'a, 'b, T> { 51 | Diff { 52 | old, 53 | new, 54 | field_path_mode: self.field_path_mode, 55 | has_changes: Cell::new(false), 56 | } 57 | } 58 | 59 | /// Writes the differences between the old and new value into the given serializer in the form 60 | /// of a sequence of diff commands 61 | pub fn diff<'a, 'b, S: Serializer, T: SerdeDiff + 'a + 'b>( 62 | self, 63 | serializer: S, 64 | old: &'a T, 65 | new: &'b T, 66 | ) -> Result { 67 | self.serializable_diff(old, new).serialize(serializer) 68 | } 69 | 70 | /// Create a deserializable Apply, where the given target will be changed when the resulting 71 | /// Apply struct is deserialized 72 | pub fn deserializable_apply<'a, T: SerdeDiff>(self, target: &'a mut T) -> Apply<'a, T> { 73 | Apply { target } 74 | } 75 | 76 | /// Applies a sequence of diff commands to the target, as read by the deserializer 77 | pub fn apply<'de, D, T: SerdeDiff>( 78 | self, 79 | deserializer: D, 80 | target: &mut T, 81 | ) -> Result<(), >::Error> 82 | where 83 | D: de::Deserializer<'de>, 84 | { 85 | deserializer.deserialize_seq(self.deserializable_apply(target)) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/counting_serializer.rs: -------------------------------------------------------------------------------- 1 | use serde::{ser, Serialize}; 2 | 3 | /// This is a serializer that counts the elements in a sequence 4 | pub(crate) struct CountingSerializer { 5 | pub(crate) num_elements: usize, 6 | } 7 | 8 | /// This is a dummy error type for CountingSerializer. Currently we don't expect the serializer 9 | /// to fail, so it's empty for now 10 | #[derive(Debug)] 11 | pub(crate) struct CountingSerializerError; 12 | 13 | impl std::fmt::Display for CountingSerializerError { 14 | fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 15 | unimplemented!() 16 | } 17 | } 18 | impl std::error::Error for CountingSerializerError { 19 | fn description(&self) -> &str { 20 | "" 21 | } 22 | fn cause(&self) -> Option<&dyn std::error::Error> { 23 | None 24 | } 25 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 26 | None 27 | } 28 | } 29 | impl ser::Error for CountingSerializerError { 30 | fn custom(_msg: T) -> Self 31 | where 32 | T: std::fmt::Display, 33 | { 34 | CountingSerializerError 35 | } 36 | } 37 | 38 | impl<'a> ser::Serializer for &'a mut CountingSerializer { 39 | type Ok = (); 40 | type Error = CountingSerializerError; 41 | 42 | type SerializeSeq = Self; 43 | type SerializeTuple = ser::Impossible<(), Self::Error>; 44 | type SerializeTupleStruct = ser::Impossible<(), Self::Error>; 45 | type SerializeTupleVariant = ser::Impossible<(), Self::Error>; 46 | type SerializeMap = ser::Impossible<(), Self::Error>; 47 | type SerializeStruct = ser::Impossible<(), Self::Error>; 48 | type SerializeStructVariant = ser::Impossible<(), Self::Error>; 49 | 50 | fn serialize_bool(self, _v: bool) -> Result<(), Self::Error> { 51 | unimplemented!() 52 | } 53 | 54 | fn serialize_i8(self, _v: i8) -> Result<(), Self::Error> { 55 | unimplemented!() 56 | } 57 | 58 | fn serialize_i16(self, _v: i16) -> Result<(), Self::Error> { 59 | unimplemented!() 60 | } 61 | 62 | fn serialize_i32(self, _v: i32) -> Result<(), Self::Error> { 63 | unimplemented!() 64 | } 65 | 66 | fn serialize_i64(self, _v: i64) -> Result<(), Self::Error> { 67 | unimplemented!() 68 | } 69 | 70 | fn serialize_u8(self, _v: u8) -> Result<(), Self::Error> { 71 | unimplemented!() 72 | } 73 | 74 | fn serialize_u16(self, _v: u16) -> Result<(), Self::Error> { 75 | unimplemented!() 76 | } 77 | 78 | fn serialize_u32(self, _v: u32) -> Result<(), Self::Error> { 79 | unimplemented!() 80 | } 81 | 82 | fn serialize_u64(self, _v: u64) -> Result<(), Self::Error> { 83 | unimplemented!() 84 | } 85 | 86 | fn serialize_f32(self, _v: f32) -> Result<(), Self::Error> { 87 | unimplemented!() 88 | } 89 | 90 | fn serialize_f64(self, _v: f64) -> Result<(), Self::Error> { 91 | unimplemented!() 92 | } 93 | 94 | fn serialize_char(self, _v: char) -> Result<(), Self::Error> { 95 | unimplemented!() 96 | } 97 | 98 | fn serialize_str(self, _v: &str) -> Result<(), Self::Error> { 99 | unimplemented!() 100 | } 101 | 102 | fn serialize_bytes(self, _v: &[u8]) -> Result<(), Self::Error> { 103 | unimplemented!() 104 | } 105 | 106 | fn serialize_none(self) -> Result<(), Self::Error> { 107 | unimplemented!() 108 | } 109 | 110 | fn serialize_some(self, _value: &T) -> Result<(), Self::Error> 111 | where 112 | T: ?Sized + Serialize, 113 | { 114 | unimplemented!() 115 | } 116 | 117 | fn serialize_unit(self) -> Result<(), Self::Error> { 118 | unimplemented!() 119 | } 120 | 121 | fn serialize_unit_struct(self, _name: &'static str) -> Result<(), Self::Error> { 122 | unimplemented!() 123 | } 124 | 125 | fn serialize_unit_variant( 126 | self, 127 | _name: &'static str, 128 | _variant_index: u32, 129 | _variant: &'static str, 130 | ) -> Result<(), Self::Error> { 131 | unimplemented!() 132 | } 133 | 134 | fn serialize_newtype_struct(self, _name: &'static str, _value: &T) -> Result<(), Self::Error> 135 | where 136 | T: ?Sized + Serialize, 137 | { 138 | unimplemented!() 139 | } 140 | 141 | fn serialize_newtype_variant( 142 | self, 143 | _name: &'static str, 144 | _variant_index: u32, 145 | _variant: &'static str, 146 | _value: &T, 147 | ) -> Result<(), Self::Error> 148 | where 149 | T: ?Sized + Serialize, 150 | { 151 | unimplemented!() 152 | } 153 | 154 | fn serialize_seq(self, _len: Option) -> Result { 155 | Ok(self) 156 | } 157 | 158 | fn serialize_tuple(self, _len: usize) -> Result { 159 | unimplemented!() 160 | } 161 | 162 | fn serialize_tuple_struct( 163 | self, 164 | _name: &'static str, 165 | _len: usize, 166 | ) -> Result { 167 | unimplemented!() 168 | } 169 | 170 | fn serialize_tuple_variant( 171 | self, 172 | _name: &'static str, 173 | _variant_index: u32, 174 | _variant: &'static str, 175 | _len: usize, 176 | ) -> Result { 177 | unimplemented!() 178 | } 179 | 180 | fn serialize_map(self, _len: Option) -> Result { 181 | unimplemented!() 182 | } 183 | 184 | fn serialize_struct( 185 | self, 186 | _name: &'static str, 187 | _len: usize, 188 | ) -> Result { 189 | unimplemented!() 190 | } 191 | 192 | fn serialize_struct_variant( 193 | self, 194 | _name: &'static str, 195 | _variant_index: u32, 196 | _variant: &'static str, 197 | _len: usize, 198 | ) -> Result { 199 | unimplemented!() 200 | } 201 | } 202 | 203 | impl<'a> ser::SerializeSeq for &'a mut CountingSerializer { 204 | type Ok = (); 205 | type Error = CountingSerializerError; 206 | 207 | fn serialize_element(&mut self, _value: &T) -> Result<(), Self::Error> 208 | where 209 | T: ?Sized + Serialize, 210 | { 211 | self.num_elements += 1; 212 | Ok(()) 213 | } 214 | 215 | fn end(self) -> Result<(), Self::Error> { 216 | Ok(()) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/difference.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | apply::ApplyContext, counting_serializer::CountingSerializer, Config, ElementStackEntry, 3 | FieldPathMode, SerdeDiff, 4 | }; 5 | use serde::{de, ser::SerializeSeq, Deserialize, Serialize, Serializer}; 6 | use std::{borrow::Cow, cell::Cell}; 7 | 8 | /// Used during a diff operation for transient data used during the diff 9 | #[doc(hidden)] 10 | pub struct DiffContext<'a, S: SerializeSeq> { 11 | /// As we descend into fields recursively, the field names (or other "placement" indicators like 12 | /// array indexes) are pushed and popped to/from this stack 13 | element_stack: Option>>, 14 | /// Reference to the serializer used to save the data 15 | serializer: &'a mut S, 16 | /// some commands are implicit Exit to save space, so we set a flag to avoid writing the next Exit 17 | implicit_exit_written: bool, 18 | /// When pushing field path elements, sometimes we need to narrow the lifetime. 19 | /// parent_element_stack contains a reference to the parent DiffContext's element_stack. 20 | parent_element_stack: Option<&'a mut Option>>>, 21 | /// Contains the minimum index in the element stack at which this context has pushed elements. 22 | /// When the context is dropped, we have to make sure we have dropped all elements 23 | /// >= index before we can pass the element stack back to the parent. 24 | /// This is to ensure the safety invariant that a sub-context's (a `reborrow`ed context) 25 | /// pushed elements cannot live longer than the sub-context itself. 26 | element_stack_start: usize, 27 | /// Mode for serializing field paths 28 | field_path_mode: FieldPathMode, 29 | /// Set to true if any change is detected 30 | has_changes: bool, 31 | } 32 | 33 | impl<'a, S: SerializeSeq> Drop for DiffContext<'a, S> { 34 | fn drop(&mut self) { 35 | if let Some(parent) = self.parent_element_stack.take() { 36 | if let Some(mut stack) = self.element_stack.take() { 37 | if self.element_stack_start < stack.len() { 38 | stack.drain(self.element_stack_start..); 39 | } 40 | parent.replace(stack); 41 | } 42 | } 43 | } 44 | } 45 | 46 | #[doc(hidden)] 47 | impl<'a, S: SerializeSeq> DiffContext<'a, S> { 48 | /// Mode for serializing field paths 49 | pub fn field_path_mode(&self) -> FieldPathMode { 50 | self.field_path_mode 51 | } 52 | 53 | /// True if a change operation has been written 54 | pub fn has_changes(&self) -> bool { 55 | self.has_changes 56 | } 57 | 58 | /// Called when we visit a field. If the structure is recursive (i.e. struct within struct, 59 | /// elements within an array) this may be called more than once before a corresponding pop_path_element 60 | /// is called. See `pop_path_element` 61 | pub fn push_field(&mut self, field_name: &'static str) { 62 | self.element_stack 63 | .as_mut() 64 | .unwrap() 65 | .push(ElementStackEntry::PathElement(DiffPathElementValue::Field( 66 | Cow::Borrowed(field_name), 67 | ))); 68 | } 69 | 70 | pub fn push_variant(&mut self, variant_name: &'static str) { 71 | self.element_stack 72 | .as_mut() 73 | .unwrap() 74 | .push(ElementStackEntry::PathElement( 75 | DiffPathElementValue::EnumVariant(Cow::Borrowed(variant_name)), 76 | )); 77 | } 78 | 79 | pub fn push_full_variant(&mut self) { 80 | self.element_stack 81 | .as_mut() 82 | .unwrap() 83 | .push(ElementStackEntry::PathElement( 84 | DiffPathElementValue::FullEnumVariant, 85 | )); 86 | } 87 | 88 | /// Called when we visit a field. If the structure is recursive (i.e. struct within struct, 89 | /// elements within an array) this may be called more than once before a corresponding pop_path_element 90 | /// is called. See `pop_path_element` 91 | pub fn push_field_index(&mut self, field_idx: u16) { 92 | self.element_stack 93 | .as_mut() 94 | .unwrap() 95 | .push(ElementStackEntry::PathElement( 96 | DiffPathElementValue::FieldIndex(field_idx), 97 | )); 98 | } 99 | 100 | /// Called when we visit an element within an indexed collection 101 | pub fn push_collection_index(&mut self, idx: usize) { 102 | self.element_stack 103 | .as_mut() 104 | .unwrap() 105 | .push(ElementStackEntry::PathElement( 106 | DiffPathElementValue::CollectionIndex(idx), 107 | )); 108 | } 109 | /// Called when we visit an element within a collection that is new 110 | pub fn push_collection_add(&mut self) { 111 | self.element_stack 112 | .as_mut() 113 | .unwrap() 114 | .push(ElementStackEntry::PathElement( 115 | DiffPathElementValue::AddToCollection, 116 | )); 117 | } 118 | 119 | pub fn push_field_element(&mut self, f: &'a dyn Fn(&mut S) -> Result<(), S::Error>) { 120 | self.element_stack 121 | .as_mut() 122 | .unwrap() 123 | .push(ElementStackEntry::Closure(f)); 124 | } 125 | 126 | /// Called when we finish visiting an element. See `push_field` for details 127 | pub fn pop_path_element(&mut self) -> Result<(), S::Error> { 128 | let element_stack = self.element_stack.as_mut().unwrap(); 129 | if element_stack.is_empty() { 130 | // if we don't have any buffered elements, we just write Exit command directly to the serializer 131 | // if we've just written a field, skip the Exit 132 | if !self.implicit_exit_written { 133 | let cmd = DiffCommandRef::<()>::Exit; 134 | self.serializer.serialize_element(&cmd) 135 | } else { 136 | self.implicit_exit_written = false; 137 | Ok(()) 138 | } 139 | } else { 140 | element_stack.pop(); 141 | self.element_stack_start = std::cmp::min(element_stack.len(), self.element_stack_start); 142 | Ok(()) 143 | } 144 | } 145 | 146 | /// Stores a value for an element that has previously been pushed using push_field or similar. 147 | pub fn save_value(&mut self, value: &T) -> Result<(), S::Error> { 148 | self.save_command(&DiffCommandRef::Value(value), true, true) 149 | } 150 | 151 | /// Stores an arbitrary DiffCommand to be handled by the type. 152 | /// Any custom sequence of DiffCommands must be followed by Exit. 153 | pub fn save_command<'b, T: Serialize>( 154 | &mut self, 155 | value: &DiffCommandRef<'b, T>, 156 | implicit_exit: bool, 157 | is_change: bool, 158 | ) -> Result<(), S::Error> { 159 | let element_stack = self.element_stack.as_mut().unwrap(); 160 | if !element_stack.is_empty() { 161 | // flush buffered elements as Enter* commands 162 | for element in element_stack.drain(0..element_stack.len()) { 163 | match element { 164 | ElementStackEntry::PathElement(element) => self 165 | .serializer 166 | .serialize_element(&DiffCommandRef::<()>::Enter(element))?, 167 | ElementStackEntry::Closure(closure) => (closure)(&mut self.serializer)?, 168 | }; 169 | } 170 | self.element_stack_start = 0; 171 | } 172 | self.has_changes |= is_change; 173 | self.implicit_exit_written = implicit_exit; 174 | self.serializer.serialize_element(value) 175 | } 176 | 177 | pub fn reborrow<'c, 'd: 'c>(&'d mut self) -> DiffContext<'c, S> 178 | where 179 | 'a: 'c, 180 | 'a: 'd, 181 | { 182 | let element_stack = self.element_stack.take(); 183 | // Some background on this then.. 184 | // HashMaps need to be able to serialize any T as keys for EnterKey(T). 185 | // The usual approach to Enter* commands is to push element paths to the element stack, 186 | // then flush the stack into the serialized stream when we encounter a value that has changed. 187 | // For any T, we need to push a type-erased closure that contains a reference to something that 188 | // might life on the stack. This is why reborrow() exists - to create a smaller scoped lifetime 189 | // that can be used to push such closures that live on the stack. 190 | // The following transmute changes the lifetime constraints on the elements in the Vec to be 191 | // limited to the lifetime of the newly created context. The ownership of the Vec is then moved 192 | // to the parent context. 193 | // Safety invariant: 194 | // In Drop, we have to make sure that any elements that could have been created by this sub-context 195 | // are removed from the Vec before passing the ownership back to the parent. 196 | 197 | let element_stack_ref = unsafe { 198 | std::mem::transmute::< 199 | &'d mut Option>>, 200 | &'c mut Option>>, 201 | >(&mut self.element_stack) 202 | }; 203 | 204 | DiffContext { 205 | element_stack_start: element_stack.as_ref().unwrap().len(), 206 | element_stack, 207 | parent_element_stack: Some(element_stack_ref), 208 | serializer: &mut *self.serializer, 209 | implicit_exit_written: self.implicit_exit_written, 210 | field_path_mode: self.field_path_mode, 211 | has_changes: false, 212 | } 213 | } 214 | } 215 | 216 | /// A serializable structure that will produce a sequence of diff commands when serialized. 217 | /// You can pass this to a serializer, or use the convenience method `diff` 218 | /// to pass your serializer along with old/new values to use when serializing the diff. 219 | /// 220 | /// # Examples 221 | /// 222 | /// ```rust 223 | /// use serde_diff::{SerdeDiff, Diff}; 224 | /// use serde::{Serialize, Deserialize}; 225 | /// #[derive(SerdeDiff, Serialize, Deserialize, PartialEq)] 226 | /// struct Test { 227 | /// a: i32, 228 | /// } 229 | /// let diff = Diff::serializable(&Test { a: 3 }, &Test { a: 5 }); 230 | /// ``` 231 | pub struct Diff<'a, 'b, T> { 232 | pub(crate) old: &'a T, 233 | pub(crate) new: &'b T, 234 | pub(crate) field_path_mode: FieldPathMode, 235 | 236 | // This is a cell to provide interior mutability 237 | pub(crate) has_changes: Cell, 238 | } 239 | 240 | impl<'a, 'b, T: SerdeDiff + 'a + 'b> Diff<'a, 'b, T> { 241 | /// Create a serializable Diff, which when serialized will write the differences between the old 242 | /// and new value into the serializer in the form of a sequence of diff commands 243 | pub fn serializable(old: &'a T, new: &'b T) -> Self { 244 | Config::default().serializable_diff(old, new) 245 | } 246 | 247 | /// Writes the differences between the old and new value into the given serializer in the form 248 | /// of a sequence of diff commands 249 | pub fn diff(serializer: S, old: &'a T, new: &'b T) -> Result { 250 | Config::default().diff(serializer, old, new) 251 | } 252 | 253 | /// True if a change was detected during the diff 254 | pub fn has_changes(&self) -> bool { 255 | self.has_changes.get() 256 | } 257 | } 258 | 259 | impl<'a, 'b, T: SerdeDiff> Serialize for Diff<'a, 'b, T> { 260 | fn serialize(&self, serializer: S) -> Result 261 | where 262 | S: Serializer, 263 | { 264 | self.has_changes.set(false); 265 | 266 | // Count the number of elements 267 | // This may only be needed for certain serializers like bincode, 268 | // so we assume that it's only required if the serializer format is not human readable. 269 | let num_elements = if !serializer.is_human_readable() { 270 | let mut serializer = CountingSerializer { num_elements: 0 }; 271 | let mut seq = serializer.serialize_seq(None).unwrap(); 272 | { 273 | let mut ctx = DiffContext { 274 | element_stack_start: 0, 275 | element_stack: Some(Vec::new()), 276 | serializer: &mut seq, 277 | implicit_exit_written: false, 278 | parent_element_stack: None, 279 | field_path_mode: self.field_path_mode, 280 | has_changes: false, 281 | }; 282 | self.old.diff(&mut ctx, &self.new).unwrap(); 283 | } 284 | seq.end().unwrap(); 285 | Some(serializer.num_elements) 286 | } else { 287 | None 288 | }; 289 | 290 | // Setup the context, starting a sequence on the serializer 291 | let mut seq = serializer.serialize_seq(num_elements)?; 292 | { 293 | let mut ctx = DiffContext { 294 | element_stack_start: 0, 295 | element_stack: Some(Vec::new()), 296 | serializer: &mut seq, 297 | implicit_exit_written: false, 298 | parent_element_stack: None, 299 | field_path_mode: self.field_path_mode, 300 | has_changes: false, 301 | }; 302 | 303 | // Do the actual comparison, writing diff commands (see DiffCommandRef, DiffCommandValue) 304 | // into the sequence 305 | self.old.diff(&mut ctx, &self.new)?; 306 | self.has_changes.set(ctx.has_changes); 307 | } 308 | 309 | // End the sequence on the serializer 310 | Ok(seq.end()?) 311 | } 312 | } 313 | 314 | pub(crate) struct DeserWrapper<'a, T> { 315 | pub(crate) val: &'a mut T, 316 | } 317 | 318 | pub(crate) struct DiffCommandDeserWrapper<'a, T> { 319 | pub(crate) val_wrapper: DeserWrapper<'a, T>, 320 | } 321 | 322 | // This monstrosity is based off the output of the derive macro for DiffCommand. 323 | // The justification for this is that we want to use Deserialize::deserialize_in_place 324 | // for DiffCommand::Value in order to support zero-copy deserialization of T. 325 | // This is achieved by passing &mut T through the DiffCommandDeserWrapper, which parses the enum 326 | // to the DeserWrapper which calls Deserialize::deserialize_in_place. 327 | pub(crate) enum DiffCommandField { 328 | Enter, 329 | Value, 330 | Remove, 331 | AddKey, 332 | EnterKey, 333 | RemoveKey, 334 | Exit, 335 | } 336 | 337 | pub(crate) struct DiffCommandFieldVisitor; 338 | 339 | const VARIANTS: &'static [&'static str] = &[ 340 | "Enter", 341 | "Value", 342 | "Remove", 343 | "AddKey", 344 | "EnterKey", 345 | "RemoveKey", 346 | "Exit", 347 | ]; 348 | 349 | impl<'de> de::Visitor<'de> for DiffCommandFieldVisitor { 350 | type Value = DiffCommandField; 351 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 352 | std::fmt::Formatter::write_str(formatter, "variant identifier") 353 | } 354 | fn visit_u64(self, value: u64) -> Result 355 | where 356 | E: de::Error, 357 | { 358 | match value { 359 | 0u64 => Ok(DiffCommandField::Enter), 360 | 1u64 => Ok(DiffCommandField::Value), 361 | 2u64 => Ok(DiffCommandField::Remove), 362 | 3u64 => Ok(DiffCommandField::AddKey), 363 | 4u64 => Ok(DiffCommandField::EnterKey), 364 | 5u64 => Ok(DiffCommandField::RemoveKey), 365 | 6u64 => Ok(DiffCommandField::Exit), 366 | _ => Err(de::Error::invalid_value( 367 | de::Unexpected::Unsigned(value), 368 | &"variant index 0 <= i < 7", 369 | )), 370 | } 371 | } 372 | fn visit_str(self, value: &str) -> Result 373 | where 374 | E: de::Error, 375 | { 376 | match value { 377 | "Enter" => Ok(DiffCommandField::Enter), 378 | "Value" => Ok(DiffCommandField::Value), 379 | "Remove" => Ok(DiffCommandField::Remove), 380 | "AddKey" => Ok(DiffCommandField::AddKey), 381 | "EnterKey" => Ok(DiffCommandField::EnterKey), 382 | "RemoveKey" => Ok(DiffCommandField::RemoveKey), 383 | "Exit" => Ok(DiffCommandField::Exit), 384 | _ => Err(de::Error::unknown_variant(value, VARIANTS)), 385 | } 386 | } 387 | fn visit_bytes(self, value: &[u8]) -> Result 388 | where 389 | E: de::Error, 390 | { 391 | match value { 392 | b"Enter" => Ok(DiffCommandField::Enter), 393 | b"Value" => Ok(DiffCommandField::Value), 394 | b"Remove" => Ok(DiffCommandField::Remove), 395 | b"AddKey" => Ok(DiffCommandField::AddKey), 396 | b"EnterKey" => Ok(DiffCommandField::EnterKey), 397 | b"RemoveKey" => Ok(DiffCommandField::RemoveKey), 398 | b"Exit" => Ok(DiffCommandField::Exit), 399 | _ => { 400 | let value = &String::from_utf8_lossy(value); 401 | Err(de::Error::unknown_variant(value, VARIANTS)) 402 | } 403 | } 404 | } 405 | } 406 | 407 | impl<'de> Deserialize<'de> for DiffCommandField { 408 | #[inline] 409 | fn deserialize(deserializer: D) -> Result 410 | where 411 | D: de::Deserializer<'de>, 412 | { 413 | de::Deserializer::deserialize_identifier(deserializer, DiffCommandFieldVisitor) 414 | } 415 | } 416 | 417 | impl<'a, 'de, T> de::DeserializeSeed<'de> for DiffCommandDeserWrapper<'a, T> 418 | where 419 | T: de::Deserialize<'de>, 420 | { 421 | type Value = DiffCommandValue<'de, T>; 422 | fn deserialize(self, deserializer: D) -> Result 423 | where 424 | D: de::Deserializer<'de>, 425 | { 426 | struct Visitor<'de, 'a, T> 427 | where 428 | T: de::Deserialize<'de>, 429 | { 430 | seed: DeserWrapper<'a, T>, 431 | lifetime: std::marker::PhantomData<&'de ()>, 432 | } 433 | impl<'de, 'a, T> de::Visitor<'de> for Visitor<'de, 'a, T> 434 | where 435 | T: de::Deserialize<'de>, 436 | { 437 | type Value = DiffCommandValue<'de, T>; 438 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 439 | std::fmt::Formatter::write_str(formatter, "enum DiffCommandValueTest") 440 | } 441 | fn visit_enum(self, data: A) -> Result 442 | where 443 | A: de::EnumAccess<'de>, 444 | { 445 | match de::EnumAccess::variant(data)? { 446 | (DiffCommandField::Enter, variant) => { 447 | let enter = 448 | de::VariantAccess::newtype_variant::(variant)?; 449 | Ok(DiffCommandValue::Enter(enter)) 450 | } 451 | (DiffCommandField::Value, variant) 452 | | (DiffCommandField::AddKey, variant) 453 | | (DiffCommandField::EnterKey, variant) 454 | | (DiffCommandField::RemoveKey, variant) => { 455 | de::VariantAccess::newtype_variant_seed::>( 456 | variant, self.seed, 457 | )?; 458 | Ok(DiffCommandValue::DeserializedValue) 459 | } 460 | (DiffCommandField::Remove, variant) => { 461 | let num_elements = de::VariantAccess::newtype_variant::(variant)?; 462 | Ok(DiffCommandValue::Remove(num_elements)) 463 | } 464 | (DiffCommandField::Exit, variant) => { 465 | de::VariantAccess::unit_variant(variant)?; 466 | Ok(DiffCommandValue::Exit) 467 | } 468 | } 469 | } 470 | } 471 | de::Deserializer::deserialize_enum( 472 | deserializer, 473 | "DiffCommandValueTest", 474 | VARIANTS, 475 | Visitor { 476 | seed: self.val_wrapper, 477 | lifetime: std::marker::PhantomData, 478 | }, 479 | ) 480 | } 481 | } 482 | 483 | impl<'a, 'de, T: Deserialize<'de>> de::DeserializeSeed<'de> for DeserWrapper<'a, T> { 484 | type Value = Self; 485 | fn deserialize(self, deserializer: D) -> Result 486 | where 487 | D: de::Deserializer<'de>, 488 | { 489 | Deserialize::deserialize_in_place(deserializer, self.val)?; 490 | Ok(self) 491 | } 492 | } 493 | 494 | // Deserializes a DiffCommand but ignores values 495 | pub(crate) struct DiffCommandIgnoreValue; 496 | 497 | impl<'de> de::DeserializeSeed<'de> for DiffCommandIgnoreValue { 498 | type Value = DiffCommandValue<'de, ()>; 499 | fn deserialize(self, deserializer: D) -> Result 500 | where 501 | D: de::Deserializer<'de>, 502 | { 503 | struct Visitor<'de> { 504 | lifetime: std::marker::PhantomData<&'de ()>, 505 | } 506 | impl<'de> de::Visitor<'de> for Visitor<'de> { 507 | type Value = DiffCommandValue<'de, ()>; 508 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 509 | std::fmt::Formatter::write_str(formatter, "enum DiffCommandValueTest") 510 | } 511 | fn visit_enum(self, data: A) -> Result 512 | where 513 | A: de::EnumAccess<'de>, 514 | { 515 | match de::EnumAccess::variant(data)? { 516 | (DiffCommandField::Enter, variant) => { 517 | let enter = 518 | de::VariantAccess::newtype_variant::(variant)?; 519 | Ok(DiffCommandValue::Enter(enter)) 520 | } 521 | (DiffCommandField::Value, variant) 522 | | (DiffCommandField::AddKey, variant) 523 | | (DiffCommandField::EnterKey, variant) 524 | | (DiffCommandField::RemoveKey, variant) => { 525 | de::VariantAccess::newtype_variant::(variant)?; 526 | Ok(DiffCommandValue::Value(())) 527 | } 528 | (DiffCommandField::Remove, variant) => { 529 | let num_elements = de::VariantAccess::newtype_variant::(variant)?; 530 | Ok(DiffCommandValue::Remove(num_elements)) 531 | } 532 | (DiffCommandField::Exit, variant) => { 533 | de::VariantAccess::unit_variant(variant)?; 534 | Ok(DiffCommandValue::Exit) 535 | } 536 | } 537 | } 538 | } 539 | de::Deserializer::deserialize_enum( 540 | deserializer, 541 | "DiffCommandValueTest", 542 | VARIANTS, 543 | Visitor { 544 | lifetime: std::marker::PhantomData, 545 | }, 546 | ) 547 | } 548 | } 549 | 550 | #[doc(hidden)] 551 | #[derive(Serialize, Debug)] 552 | pub enum DiffCommandRef<'a, T: Serialize> { 553 | /// Enter a path element 554 | Enter(DiffPathElementValue<'a>), 555 | /// A value to be deserialized. 556 | /// For collections, this implies "add to end" if not preceded by UpdateIndex. 557 | Value(&'a T), 558 | /// Remove N items from end of collection 559 | Remove(usize), 560 | /// Add a key to a map 561 | AddKey(&'a T), 562 | /// Enter a key in a map. 563 | /// This isn't part of Enter because then DiffPathElementValue would have to be generic over T 564 | // Fortunately, keys on HashMaps are terminal as far as we're concerned. 565 | EnterKey(&'a T), 566 | /// Remove a key from a map 567 | RemoveKey(&'a T), 568 | /// Exit a path element 569 | Exit, 570 | } 571 | 572 | #[doc(hidden)] 573 | #[derive(Deserialize, Debug)] 574 | pub enum DiffCommandValue<'a, T> { 575 | // Enter a path element 576 | #[serde(borrow)] 577 | Enter(DiffPathElementValue<'a>), 578 | /// A value to be deserialized. 579 | Value(T), 580 | /// Remove N items from end of collection 581 | Remove(usize), 582 | /// Add a key to a map 583 | AddKey(T), 584 | // Enter a key in a map 585 | EnterKey(T), 586 | /// Remove a key from a map 587 | RemoveKey(T), 588 | // Exit a path element 589 | Exit, 590 | // Never serialized 591 | Nothing, 592 | // Never serialized, used to indicate that deserializer wrote a value into supplied reference 593 | DeserializedValue, 594 | } 595 | 596 | #[doc(hidden)] 597 | #[derive(Serialize, Deserialize, Debug)] 598 | pub enum DiffPathElementValue<'a> { 599 | /// A struct field 600 | #[serde(borrow)] 601 | Field(Cow<'a, str>), 602 | FieldIndex(u16), 603 | EnumVariant(Cow<'a, str>), 604 | FullEnumVariant, 605 | CollectionIndex(usize), 606 | AddToCollection, 607 | } 608 | 609 | impl Deserialize<'a>> SerdeDiff for Vec { 610 | fn diff<'a, S: SerializeSeq>( 611 | &self, 612 | ctx: &mut DiffContext<'a, S>, 613 | other: &Self, 614 | ) -> Result { 615 | let mut self_iter = self.iter(); 616 | let mut other_iter = other.iter(); 617 | let mut idx = 0; 618 | let mut need_exit = false; 619 | let mut changed = false; 620 | loop { 621 | let self_item = self_iter.next(); 622 | let other_item = other_iter.next(); 623 | match (self_item, other_item) { 624 | (None, None) => break, 625 | (Some(_), None) => { 626 | let mut num_to_remove = 1; 627 | while self_iter.next().is_some() { 628 | num_to_remove += 1; 629 | } 630 | ctx.save_command::<()>(&DiffCommandRef::Remove(num_to_remove), true, true)?; 631 | changed = true; 632 | need_exit = false; 633 | } 634 | (None, Some(other_item)) => { 635 | ctx.save_command::<()>( 636 | &DiffCommandRef::Enter(DiffPathElementValue::AddToCollection), 637 | false, 638 | true, 639 | )?; 640 | ctx.save_command(&DiffCommandRef::Value(other_item), true, true)?; 641 | need_exit = true; 642 | changed = true; 643 | } 644 | (Some(self_item), Some(other_item)) => { 645 | ctx.push_collection_index(idx); 646 | if ::diff(self_item, ctx, other_item)? { 647 | need_exit = true; 648 | changed = true; 649 | } 650 | ctx.pop_path_element()?; 651 | } 652 | } 653 | idx += 1; 654 | } 655 | if need_exit { 656 | ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?; 657 | } 658 | Ok(changed) 659 | } 660 | 661 | fn apply<'de, A>( 662 | &mut self, 663 | seq: &mut A, 664 | ctx: &mut ApplyContext, 665 | ) -> Result>::Error> 666 | where 667 | A: de::SeqAccess<'de>, 668 | { 669 | let mut changed = false; 670 | while let Some(cmd) = ctx.read_next_command::(seq)? { 671 | use DiffCommandValue::*; 672 | use DiffPathElementValue::*; 673 | match cmd { 674 | // we should not be getting fields when reading collection commands 675 | Enter(Field(_)) => { 676 | ctx.skip_value(seq)?; 677 | break; 678 | } 679 | Enter(CollectionIndex(idx)) => { 680 | if let Some(value_ref) = self.get_mut(idx) { 681 | changed |= ::apply(value_ref, seq, ctx)?; 682 | } else { 683 | ctx.skip_value(seq)?; 684 | } 685 | } 686 | Enter(AddToCollection) => { 687 | if let Value(v) = ctx 688 | .read_next_command(seq)? 689 | .expect("Expected value after AddToCollection") 690 | { 691 | changed = true; 692 | self.push(v); 693 | } else { 694 | panic!("Expected value after AddToCollection"); 695 | } 696 | } 697 | Remove(num_elements) => { 698 | let new_length = self.len().saturating_sub(num_elements); 699 | self.truncate(new_length); 700 | changed = true; 701 | break; 702 | } 703 | _ => break, 704 | } 705 | } 706 | Ok(changed) 707 | } 708 | } 709 | -------------------------------------------------------------------------------- /src/implementation.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | apply::ApplyContext, 3 | difference::{DiffCommandRef, DiffContext, DiffPathElementValue}, 4 | SerdeDiff, 5 | }; 6 | 7 | use serde::{de, ser::SerializeSeq, Deserialize, Serialize}; 8 | 9 | use std::{ 10 | collections::{BTreeMap, HashMap}, 11 | hash::Hash, 12 | }; 13 | 14 | macro_rules! array_impls { 15 | ($($len:tt)+) => { 16 | $( 17 | impl serde::Deserialize<'a>> $crate::SerdeDiff for [T; $len] { 18 | fn diff<'a, S: serde::ser::SerializeSeq>( 19 | &self, 20 | ctx: &mut $crate::difference::DiffContext<'a, S>, 21 | other: &Self, 22 | ) -> Result { 23 | use $crate::difference::DiffCommandRef; 24 | 25 | let mut need_exit = false; 26 | let mut changed = false; 27 | for (idx, (self_item, other_item)) in self.iter().zip(other.iter()).enumerate() { 28 | ctx.push_collection_index(idx); 29 | if ::diff(self_item, ctx, other_item)? { 30 | need_exit = true; 31 | changed = true; 32 | } 33 | ctx.pop_path_element()?; 34 | } 35 | if need_exit { 36 | ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?; 37 | } 38 | Ok(changed) 39 | } 40 | 41 | fn apply<'de, A>( 42 | &mut self, 43 | seq: &mut A, 44 | ctx: &mut $crate::apply::ApplyContext, 45 | ) -> Result>::Error> 46 | where 47 | A: serde::de::SeqAccess<'de>, 48 | { 49 | let mut changed = false; 50 | while let Some(cmd) = ctx.read_next_command::(seq)? { 51 | use $crate::difference::DiffCommandValue::*; 52 | use $crate::difference::DiffPathElementValue::*; 53 | match cmd { 54 | // we should not be getting fields when reading collection commands 55 | Enter(Field(_)) => { 56 | ctx.skip_value(seq)?; 57 | break; 58 | } 59 | Enter(CollectionIndex(idx)) => { 60 | if let Some(value_ref) = self.get_mut(idx) { 61 | changed |= ::apply(value_ref, seq, ctx)?; 62 | } else { 63 | ctx.skip_value(seq)?; 64 | } 65 | } 66 | _ => break, 67 | } 68 | } 69 | Ok(changed) 70 | } 71 | } 72 | )+ 73 | } 74 | } 75 | 76 | array_impls! { 77 | 01 02 03 04 05 06 07 08 09 10 78 | 11 12 13 14 15 16 17 18 19 20 79 | 21 22 23 24 25 26 27 28 29 30 80 | 31 32 81 | 40 48 50 56 64 72 96 100 128 160 192 200 224 256 384 512 82 | 768 1024 2048 4096 8192 16384 32768 65536 83 | } 84 | 85 | macro_rules! tuple_impls { 86 | ($($len:expr => ($($n:tt $name:ident)+))+) => { 87 | $( 88 | impl<$($name),+> $crate::SerdeDiff for ($($name,)+) 89 | where 90 | $($name: $crate::SerdeDiff + serde::Serialize + for<'a> serde::Deserialize<'a>,)+ 91 | { 92 | fn diff<'a, S: serde::ser::SerializeSeq>( 93 | &self, 94 | ctx: &mut $crate::difference::DiffContext<'a, S>, 95 | other: &Self, 96 | ) -> Result { 97 | let mut changed = false; 98 | $( 99 | ctx.push_field(stringify!($n)); 100 | changed |= <$name as $crate::SerdeDiff>::diff(&self.$n, ctx, &other.$n)?; 101 | ctx.pop_path_element()?; 102 | )+ 103 | Ok(changed) 104 | } 105 | 106 | fn apply<'de, A>( 107 | &mut self, 108 | seq: &mut A, 109 | ctx: &mut $crate::apply::ApplyContext, 110 | ) -> Result>::Error> 111 | where 112 | A: serde::de::SeqAccess<'de>, 113 | { 114 | let mut changed = false; 115 | while let Some($crate::difference::DiffPathElementValue::Field(element)) = ctx.next_path_element(seq)? { 116 | match element.as_ref() { 117 | $( 118 | stringify!($n) => changed |= <$name as $crate::SerdeDiff>::apply(&mut self.$n, seq, ctx)?, 119 | )+ 120 | _ => ctx.skip_value(seq)?, 121 | } 122 | } 123 | Ok(changed) 124 | } 125 | } 126 | )+ 127 | } 128 | } 129 | 130 | tuple_impls! { 131 | 1 => (0 T0) 132 | 2 => (0 T0 1 T1) 133 | 3 => (0 T0 1 T1 2 T2) 134 | 4 => (0 T0 1 T1 2 T2 3 T3) 135 | 5 => (0 T0 1 T1 2 T2 3 T3 4 T4) 136 | 6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5) 137 | 7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6) 138 | 8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7) 139 | 9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8) 140 | 10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9) 141 | 11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10) 142 | 12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11) 143 | 13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12) 144 | 14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13) 145 | 15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14) 146 | 16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15) 147 | } 148 | /// Implement SerdeDiff on a "map-like" type such as HashMap. 149 | macro_rules! map_serde_diff { 150 | ($t:ty, $($extra_traits:path),*) => { 151 | impl SerdeDiff for $t 152 | where 153 | K: SerdeDiff + Serialize + for<'a> Deserialize<'a> $(+ $extra_traits)*, // + Hash + Eq, 154 | V: SerdeDiff + Serialize + for<'a> Deserialize<'a>, 155 | { 156 | fn diff<'a, S: SerializeSeq>( 157 | &self, 158 | ctx: &mut $crate::difference::DiffContext<'a, S>, 159 | other: &Self, 160 | ) -> Result { 161 | use $crate::difference::DiffCommandRef; 162 | 163 | let mut changed = false; 164 | 165 | // TODO: detect renames 166 | for (key, self_value) in self.iter() { 167 | match other.get(key) { 168 | Some(other_value) => { 169 | let save_closure = |serializer: &mut S| serializer.serialize_element(&DiffCommandRef::EnterKey(key)); 170 | let mut subctx = ctx.reborrow(); 171 | subctx.push_field_element(&save_closure); 172 | if ::diff(self_value, &mut subctx, other_value)? { 173 | changed = true; 174 | } 175 | }, 176 | None => { 177 | ctx.save_command(&DiffCommandRef::RemoveKey(key), true, true)?; 178 | changed = true; 179 | }, 180 | } 181 | } 182 | 183 | for (key, other_value) in other.iter() { 184 | if !self.contains_key(key) { 185 | ctx.save_command(&DiffCommandRef::AddKey(key), true, true)?; 186 | ctx.save_command(&DiffCommandRef::Value(other_value), true, true)?; 187 | changed = true; 188 | } 189 | } 190 | 191 | if changed { 192 | ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?; 193 | } 194 | Ok(changed) 195 | } 196 | 197 | fn apply<'de, A>( 198 | &mut self, 199 | seq: &mut A, 200 | ctx: &mut ApplyContext, 201 | ) -> Result>::Error> 202 | where 203 | A: de::SeqAccess<'de>, 204 | { 205 | let mut changed = false; 206 | while let Some(cmd) = ctx.read_next_command::(seq)? { 207 | use $crate::difference::DiffCommandValue::*; 208 | use $crate::difference::DiffPathElementValue::*; 209 | match cmd { 210 | // we should not be getting fields when reading collection commands 211 | Enter(Field(_)) => { 212 | ctx.skip_value(seq)?; 213 | break; 214 | } 215 | AddKey(key) => if let Some(Value(v)) = ctx.read_next_command(seq)? { 216 | //changed |= self.insert(key, v).map(|old_val| v != old_val).unwrap_or(true); 217 | self.insert(key, v); 218 | changed = true; 219 | } else { 220 | panic!("Expected value after AddKey"); 221 | } 222 | EnterKey(key) => if let Some(value_ref) = self.get_mut(&key) { 223 | changed |= ::apply(value_ref, seq, ctx)?; 224 | } else { 225 | ctx.skip_value(seq)?; 226 | } 227 | RemoveKey(key) => changed |= self.remove(&key).is_some(), 228 | _ => break, 229 | } 230 | } 231 | Ok(changed) 232 | } 233 | } 234 | }; 235 | } 236 | 237 | map_serde_diff!(HashMap, Hash, Eq); 238 | map_serde_diff!(BTreeMap, Ord); 239 | 240 | /// Implements SerdeDiff on a type given that it impls Serialize + Deserialize + PartialEq. 241 | /// This makes the type a "terminal" type in the SerdeDiff hierarchy, meaning deeper inspection 242 | /// will not be possible. Use the SerdeDiff derive macro for recursive field inspection. 243 | #[macro_export] 244 | macro_rules! opaque_serde_diff { 245 | ($t:ty) => { 246 | impl SerdeDiff for $t { 247 | fn diff<'a, S: $crate::_serde::ser::SerializeSeq>( 248 | &self, 249 | ctx: &mut $crate::DiffContext<'a, S>, 250 | other: &Self, 251 | ) -> Result { 252 | if self != other { 253 | ctx.save_value(other)?; 254 | Ok(true) 255 | } else { 256 | Ok(false) 257 | } 258 | } 259 | 260 | fn apply<'de, A>( 261 | &mut self, 262 | seq: &mut A, 263 | ctx: &mut $crate::ApplyContext, 264 | ) -> Result>::Error> 265 | where 266 | A: $crate::_serde::de::SeqAccess<'de>, 267 | { 268 | ctx.read_value(seq, self) 269 | } 270 | } 271 | }; 272 | } 273 | 274 | // Implement `SerdeDiff` for primitive types and types defined in the standard library. 275 | opaque_serde_diff!(bool); 276 | opaque_serde_diff!(isize); 277 | opaque_serde_diff!(i8); 278 | opaque_serde_diff!(i16); 279 | opaque_serde_diff!(i32); 280 | opaque_serde_diff!(i64); 281 | opaque_serde_diff!(usize); 282 | opaque_serde_diff!(u8); 283 | opaque_serde_diff!(u16); 284 | opaque_serde_diff!(u32); 285 | opaque_serde_diff!(u64); 286 | opaque_serde_diff!(i128); 287 | opaque_serde_diff!(u128); 288 | opaque_serde_diff!(f32); 289 | opaque_serde_diff!(f64); 290 | opaque_serde_diff!(char); 291 | opaque_serde_diff!(String); 292 | opaque_serde_diff!(std::ffi::CString); 293 | #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] 294 | opaque_serde_diff!(std::ffi::OsString); 295 | opaque_serde_diff!(std::num::NonZeroU8); 296 | opaque_serde_diff!(std::num::NonZeroU16); 297 | opaque_serde_diff!(std::num::NonZeroU32); 298 | opaque_serde_diff!(std::num::NonZeroU64); 299 | opaque_serde_diff!(std::time::Duration); 300 | opaque_serde_diff!(std::time::SystemTime); 301 | opaque_serde_diff!(std::net::IpAddr); 302 | opaque_serde_diff!(std::net::Ipv4Addr); 303 | opaque_serde_diff!(std::net::Ipv6Addr); 304 | opaque_serde_diff!(std::net::SocketAddr); 305 | opaque_serde_diff!(std::net::SocketAddrV4); 306 | opaque_serde_diff!(std::net::SocketAddrV6); 307 | opaque_serde_diff!(std::path::PathBuf); 308 | 309 | impl Deserialize<'a>> SerdeDiff for Option { 310 | fn diff<'a, S: SerializeSeq>( 311 | &self, 312 | ctx: &mut DiffContext<'a, S>, 313 | other: &Self, 314 | ) -> Result { 315 | let mut self_iter = self.iter(); 316 | let mut other_iter = other.iter(); 317 | let mut idx = 0; 318 | let mut need_exit = false; 319 | let mut changed = false; 320 | loop { 321 | let self_item = self_iter.next(); 322 | let other_item = other_iter.next(); 323 | match (self_item, other_item) { 324 | (None, None) => break, 325 | (Some(_), None) => { 326 | let mut num_to_remove = 1; 327 | while self_iter.next().is_some() { 328 | num_to_remove += 1; 329 | } 330 | ctx.save_command::<()>(&DiffCommandRef::Remove(num_to_remove), true, true)?; 331 | changed = true; 332 | } 333 | (None, Some(other_item)) => { 334 | ctx.save_command::<()>( 335 | &DiffCommandRef::Enter(DiffPathElementValue::AddToCollection), 336 | false, 337 | true, 338 | )?; 339 | ctx.save_command(&DiffCommandRef::Value(other_item), true, true)?; 340 | need_exit = true; 341 | changed = true; 342 | } 343 | (Some(self_item), Some(other_item)) => { 344 | ctx.push_collection_index(idx); 345 | if ::diff(self_item, ctx, other_item)? { 346 | need_exit = true; 347 | changed = true; 348 | } 349 | ctx.pop_path_element()?; 350 | } 351 | } 352 | idx += 1; 353 | } 354 | if need_exit { 355 | ctx.save_command::<()>(&DiffCommandRef::Exit, true, false)?; 356 | } 357 | Ok(changed) 358 | } 359 | 360 | fn apply<'de, A>( 361 | &mut self, 362 | seq: &mut A, 363 | ctx: &mut ApplyContext, 364 | ) -> Result>::Error> 365 | where 366 | A: de::SeqAccess<'de>, 367 | { 368 | let mut changed = false; 369 | while let Some(cmd) = ctx.read_next_command::(seq)? { 370 | use crate::difference::DiffCommandValue::*; 371 | use DiffPathElementValue::*; 372 | 373 | match cmd { 374 | // we should not be getting fields when reading collection commands 375 | Enter(Field(_)) => { 376 | ctx.skip_value(seq)?; 377 | break; 378 | } 379 | Enter(CollectionIndex(0)) => { 380 | if let Some(value_ref) = self { 381 | changed |= ::apply(value_ref, seq, ctx)?; 382 | } else { 383 | ctx.skip_value(seq)?; 384 | } 385 | } 386 | Enter(AddToCollection) => { 387 | if let Value(v) = ctx 388 | .read_next_command(seq)? 389 | .expect("Expected value after AddToCollection") 390 | { 391 | debug_assert!(self.is_none()); 392 | changed = true; 393 | *self = Some(v); 394 | } else { 395 | panic!("Expected value after AddToCollection"); 396 | } 397 | } 398 | Remove(1) => { 399 | *self = None; 400 | changed = true; 401 | break; 402 | } 403 | _ => break, 404 | } 405 | } 406 | Ok(changed) 407 | } 408 | } 409 | 410 | type Unit = (); 411 | opaque_serde_diff!(Unit); 412 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs)] 2 | //! A small helper that can 3 | //! 1. Serialize the fields that differ between two structs of the same type 4 | //! 2. Apply previously serialized field differences to other structs 5 | //! 6 | //! The SerdeDiff trait impl can serialize field paths recursively, 7 | //! greatly reducing the amount of data that needs to be serialized 8 | //! when only a small part of a struct has changed. 9 | 10 | #[cfg(test)] 11 | mod tests; 12 | 13 | #[doc(hidden)] 14 | pub use serde as _serde; 15 | use serde::{de, ser::SerializeSeq}; 16 | pub use serde_diff_derive::SerdeDiff; 17 | 18 | #[doc(hidden)] 19 | pub(crate) mod apply; 20 | pub(crate) mod config; 21 | pub(crate) mod counting_serializer; 22 | #[doc(hidden)] 23 | pub(crate) mod difference; 24 | pub(crate) mod implementation; 25 | 26 | pub use apply::Apply; 27 | pub use config::Config; 28 | pub use difference::Diff; 29 | 30 | // Used by the proc_macro 31 | pub use apply::ApplyContext; 32 | pub use difference::DiffContext; 33 | pub use difference::DiffPathElementValue; 34 | 35 | // NEXT STEPS: 36 | // - Decouple from serde_json as much as possible. We might need to use a "stream" format with 37 | // well-defined data order to be able to use serde Deserializer trait. DONE 38 | // - Make all fields work again. DONE 39 | // - Make it work via proc macro. DONE 40 | // - Blanket impl or impl-via-macro common std types (i.e f32, i32, String). DONE 41 | // - Handle containers. DONE 42 | // - Ignore type mismatches instead of propagating the error. IMPOSSIBLE?? 43 | // 44 | // A problem occurs when encoding the command stream for bincode: 45 | // We need to know the size of the list before we start serializing. 46 | // To do so, we need to implement the serde::ser::Serializer trait and 47 | // make the implementation only count up every time an element is serialized, doing nothing else. 48 | // This is implemented as CountingSerializer 49 | 50 | /// Anything diffable implements this trait 51 | pub trait SerdeDiff { 52 | /// Recursively walk the struct, invoking serialize_element on each member if the element is 53 | /// different. Returns true if any changes exist, otherwise false. After this call, the 54 | /// DiffContext will contain the data that has changed. 55 | fn diff<'a, S: SerializeSeq>( 56 | &self, 57 | ctx: &mut difference::DiffContext<'a, S>, 58 | other: &Self, 59 | ) -> Result; 60 | 61 | /// Applies the diff to the struct. Returns true if the struct was changed, otherwise false. 62 | fn apply<'de, A>( 63 | &mut self, 64 | seq: &mut A, 65 | ctx: &mut apply::ApplyContext, 66 | ) -> Result>::Error> 67 | where 68 | A: de::SeqAccess<'de>; 69 | } 70 | 71 | /// Configures how to serialize field identifiers 72 | #[derive(Copy, Clone)] 73 | pub enum FieldPathMode { 74 | /// Use the field's string name as its identifier 75 | Name, 76 | /// Use the field's index in the struct as its identifier 77 | Index, 78 | } 79 | 80 | pub(crate) enum ElementStackEntry<'a, S: SerializeSeq> { 81 | PathElement(difference::DiffPathElementValue<'a>), 82 | Closure(&'a dyn Fn(&mut S) -> Result<(), S::Error>), 83 | } 84 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate as serde_diff; 2 | use crate::{Apply, Diff, SerdeDiff}; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::Debug; 5 | 6 | #[derive(SerdeDiff, Serialize, Deserialize, PartialEq, Debug, Copy, Clone)] 7 | struct TestStruct { 8 | a: u32, 9 | b: f64, 10 | } 11 | 12 | fn roundtrip Deserialize<'a> + PartialEq + Debug + Clone>( 13 | old: T, 14 | new: T, 15 | ) { 16 | let diff = Diff::serializable(&old, &new); 17 | let json_diff = serde_json::to_string(&diff).unwrap(); 18 | let mut deserializer = serde_json::Deserializer::from_str(&json_diff); 19 | let mut target = old.clone(); 20 | Apply::apply(&mut deserializer, &mut target).unwrap(); 21 | assert_eq!(target, new); 22 | 23 | let bincode_diff = bincode::serialize(&diff).unwrap(); 24 | let mut target = old; 25 | 26 | bincode::config() 27 | .deserialize_seed(Apply::deserializable(&mut target), &bincode_diff) 28 | .unwrap(); 29 | assert_eq!(target, new); 30 | } 31 | 32 | fn partial Deserialize<'a> + PartialEq + Debug + Clone>( 33 | old: T, 34 | new: T, 35 | target: T, 36 | expected: T, 37 | ) { 38 | let diff = Diff::serializable(&old, &new); 39 | let json_diff = serde_json::to_string(&diff).unwrap(); 40 | let mut deserializer = serde_json::Deserializer::from_str(&json_diff); 41 | let mut tmp_target = target.clone(); 42 | Apply::apply(&mut deserializer, &mut tmp_target).unwrap(); 43 | assert_eq!(tmp_target, expected); 44 | 45 | let bincode_diff = bincode::serialize(&diff).unwrap(); 46 | let mut tmp_target = target; 47 | bincode::config() 48 | .deserialize_seed(Apply::deserializable(&mut tmp_target), &bincode_diff) 49 | .unwrap(); 50 | assert_eq!(tmp_target, expected); 51 | } 52 | 53 | #[test] 54 | fn test_option() { 55 | roundtrip(None::, None); 56 | roundtrip(None, Some(TestStruct { a: 42, b: 12. })); 57 | roundtrip(Some(TestStruct { a: 42, b: 12. }), None); 58 | roundtrip( 59 | Some(TestStruct { a: 52, b: 32. }), 60 | Some(TestStruct { a: 42, b: 12. }), 61 | ); 62 | 63 | partial( 64 | Some(TestStruct { a: 5, b: 2. }), 65 | Some(TestStruct { a: 8, b: 2. }), 66 | Some(TestStruct { a: 0, b: 4. }), 67 | Some(TestStruct { a: 8, b: 4. }), 68 | ); 69 | } 70 | 71 | #[test] 72 | fn test_array() { 73 | partial([0, 1, 2, 3], [0, 1, 9, 3], [4, 5, 6, 7], [4, 5, 9, 7]); 74 | 75 | partial( 76 | Some([0, 1, 2, 3]), 77 | Some([0, 1, 9, 3]), 78 | Some([4, 5, 6, 7]), 79 | Some([4, 5, 9, 7]), 80 | ); 81 | 82 | partial( 83 | [ 84 | None, 85 | Some(TestStruct { a: 5, b: 2. }), 86 | Some(TestStruct { a: 5, b: 2. }), 87 | ], 88 | [ 89 | Some(TestStruct { a: 8, b: 2. }), 90 | Some(TestStruct { a: 8, b: 2. }), 91 | None, 92 | ], 93 | [None, Some(TestStruct { a: 0, b: 4. }), None], 94 | [ 95 | Some(TestStruct { a: 8, b: 2. }), 96 | Some(TestStruct { a: 8, b: 4. }), 97 | None, 98 | ], 99 | ); 100 | } 101 | 102 | #[test] 103 | fn test_tuple() { 104 | roundtrip( 105 | (None::, Some(TestStruct { a: 8, b: 2. })), 106 | (None, Some(TestStruct { a: 8, b: 2. })), 107 | ); 108 | 109 | partial((0, 1, 2, 3), (0, 1, 9, 3), (4, 5, 6, 7), (4, 5, 9, 7)); 110 | 111 | partial( 112 | Some((0, 1, 2, 3)), 113 | Some((0, 1, 9, 3)), 114 | Some((4, 5, 6, 7)), 115 | Some((4, 5, 9, 7)), 116 | ); 117 | 118 | partial( 119 | ( 120 | None, 121 | Some(TestStruct { a: 5, b: 2. }), 122 | Some(TestStruct { a: 5, b: 2. }), 123 | ), 124 | ( 125 | Some(TestStruct { a: 8, b: 2. }), 126 | Some(TestStruct { a: 8, b: 2. }), 127 | None, 128 | ), 129 | (None, Some(TestStruct { a: 0, b: 4. }), None), 130 | ( 131 | Some(TestStruct { a: 8, b: 2. }), 132 | Some(TestStruct { a: 8, b: 4. }), 133 | None, 134 | ), 135 | ); 136 | } 137 | 138 | #[derive(SerdeDiff, Serialize, Deserialize, Clone, PartialEq, Debug)] 139 | #[serde(from = "MySimpleStruct", into = "MySimpleStruct")] 140 | #[serde_diff(target = "MySimpleStruct")] 141 | struct MyComplexStruct { 142 | // This field will be serialized 143 | a: u32, 144 | // This field will not be serialized, because it is not needed for 145 | b: u32, 146 | } 147 | 148 | #[derive(SerdeDiff, Serialize, Deserialize, Default, PartialEq, Debug)] 149 | #[serde(rename = "MyComplexStruct", default)] 150 | struct MySimpleStruct { 151 | a: u32, 152 | } 153 | 154 | impl From for MyComplexStruct { 155 | fn from(my_simple_struct: MySimpleStruct) -> Self { 156 | MyComplexStruct { 157 | a: my_simple_struct.a, 158 | b: 0, // this value wasn't serialized, so we'll just default it to zero 159 | } 160 | } 161 | } 162 | 163 | impl Into for MyComplexStruct { 164 | fn into(self) -> MySimpleStruct { 165 | MySimpleStruct { a: self.a } 166 | } 167 | } 168 | 169 | fn targeted_roundtrip(old: T, new: T, expected: T) 170 | where 171 | T: SerdeDiff + Serialize + for<'a> Deserialize<'a> + PartialEq + Debug + Clone, 172 | U: SerdeDiff + Serialize + for<'a> Deserialize<'a>, 173 | { 174 | let diff = Diff::serializable(&old, &new); 175 | let json_diff = serde_json::to_string(&diff).unwrap(); 176 | let mut deserializer = serde_json::Deserializer::from_str(&json_diff); 177 | let mut applied = old.clone(); 178 | Apply::apply(&mut deserializer, &mut applied).unwrap(); 179 | assert_eq!(applied, expected); 180 | 181 | let bincode_diff = bincode::serialize(&diff).unwrap(); 182 | let mut applied = old; 183 | 184 | bincode::config() 185 | .deserialize_seed(Apply::deserializable(&mut applied), &bincode_diff) 186 | .unwrap(); 187 | assert_eq!(applied, expected); 188 | } 189 | 190 | #[test] 191 | fn test_targeted() { 192 | targeted_roundtrip::( 193 | MyComplexStruct { a: 1, b: 777 }, 194 | MyComplexStruct { a: 2, b: 999 }, 195 | MyComplexStruct { a: 2, b: 0 }, 196 | ); 197 | targeted_roundtrip::, Option>( 198 | Some(MyComplexStruct { a: 1, b: 777 }), 199 | Some(MyComplexStruct { a: 2, b: 999 }), 200 | Some(MyComplexStruct { a: 2, b: 0 }), 201 | ); 202 | } 203 | --------------------------------------------------------------------------------