27 releases (8 breaking)
new 0.9.0 | Mar 25, 2025 |
---|---|
0.8.6 | Oct 2, 2024 |
0.6.0 | Jul 29, 2024 |
0.4.1 | Oct 2, 2023 |
0.2.3 | Mar 4, 2023 |
#31 in #overlay
11,347 downloads per month
Used in 4 crates
(via struct-patch)
39KB
871 lines
Struct Patch
A lib help you patch Rust instance, and easy to partial update configures.
Introduction
This crate provides the Patch
, Filler
traits and accompanying derive macro.
If the any field in Patch
is some then it will overwrite the field of instance when apply.
If the any field in the instance is none then it will try to fill the field with the Filler
.
Currently, Filler
only support Option
field, and the Vec
and other field will implement later.
The detail discussion is in issue #81
Quick Example
Deriving Patch
on a struct will generate a struct similar to the original one, but with all fields wrapped in an Option
.
An instance of such a patch struct can be applied onto the original struct, replacing values only if they are set to Some
, leaving them unchanged otherwise.
use struct_patch::Patch;
use serde::{Deserialize, Serialize};
#[derive(Default, Debug, PartialEq, Patch)]
#[patch(attribute(derive(Debug, Default, Deserialize, Serialize)))]
struct Item {
field_bool: bool,
field_int: usize,
field_string: String,
}
fn patch_json() {
let mut item = Item {
field_bool: true,
field_int: 42,
field_string: String::from("hello"),
};
let data = r#"{
"field_int": 7
}"#;
let patch: ItemPatch = serde_json::from_str(data).unwrap();
item.apply(patch);
// You can do
// `let new_item = item << patch;`
// For multiple patches,
// you can do this
// `let new_item = item << patch_1 << patch_2;`
// or make an aggregated one, but please make sure the patch fields do not conflict, else will panic
// ```
// let overall_patch = patch_1 + patch_2 + patch_3;
// let new_item = item << overall_patch;
// ```
assert_eq!(
item,
Item {
field_bool: true,
field_int: 7,
field_string: String::from("hello")
}
);
}
Deriving Filler
on a struct will generate a struct similar to the original one with the field with Option
. Unlike Patch
, the Filler
only work on the empty fields of instance.
use struct_patch::Filler;
#[derive(Filler)]
struct Item {
field_int: usize,
maybe_field_int: Option<usize>,
}
let mut item = Item {
field_int: 0,
maybe_field_int: None,
};
let filler_1 = ItemFiller{ maybe_field_int: Some(7), };
item.apply(filler_1);
assert_eq!(item.maybe_field_int, Some(7));
let filler_2 = ItemFiller{ maybe_field_int: Some(100), };
// The field is not empty, so the filler has not effect.
item.apply(filler_2);
assert_eq!(item.maybe_field_int, Some(7));
Documentation and Examples
Also, you can modify the patch structure by defining #[patch(...)]
attributes on the original struct or fields.
Struct attributes:
#[patch(name = "...")]
: change the name of the generated patch struct.#[patch(attribute(...))]
: add attributes to the generated patch struct.#[patch(attribute(derive(...)))]
: add derives to the generated patch struct.
Field attributes:
#[patch(skip)]
: skip the field in the generated patch struct.#[patch(name = "...")]
: change the type of the field in the generated patch struct.#[patch(attribute(...))]
: add attributes to the field in the generated patch struct.#[patch(attribute(derive(...)))]
: add derives to the field in the generated patch struct.
Please check the traits of document to learn more.
The examples demo following scenarios.
- diff two instance for a patch
- create a patch from json string
- rename the patch structure
- check a patch is empty or not
- add attribute to patch struct
- show option field behavior
- show operators about patches
- show example with serde crates, ex:
humantime_serde
for duration
Features
This crate also includes the following optional features:
status
(default): implements theStatus
trait for the patch struct, which provides theis_empty
method.op
(default): provide operators<<
between instance and patch, and+
for patches- default: when there is a field conflict between patches,
+
will add together if the#[patch(addable)]
or#[patch(add=fn)]
is provided, else it will panic. merge
(optional): implements theMerge
trait for the patch struct, which provides themerge
method, and<<
between patches.
- default: when there is a field conflict between patches,
std
(optional):box
: implements thePatch<Box<P>>
trait forT
whereT
implementsPatch<P>
. This let you patch a boxed (or not) struct with a boxed patch.option
: implements thePatch<Option<P>>
trait forOption<T>
whereT
implementsPatch<P>
, please take a look at the example to learn more.- default:
T
needs to implementFrom<P>
. When patching on None, it will based onfrom<P>
to cast T, and this let you patch structs containing fields with optional values. none_as_default
:T
needs to implementDefault
. When patching on None, it will patch on a default instance, and this also let you patch structs containing fields with optional values.keep_none
: When patching on None, it is still None.
- default:
Dependencies
~200–630KB
~15K SLoC