#patch #struct #no-alloc

macro no-std rust-patch-derive

Derive macro implementation for rust-patch

4 releases

0.1.3 Feb 27, 2023
0.1.2 Feb 23, 2023
0.1.1 Feb 23, 2023
0.1.0 Feb 22, 2023

#79 in #patch

Download history 303/week @ 2023-11-20 433/week @ 2023-11-27 356/week @ 2023-12-04 414/week @ 2023-12-11 589/week @ 2023-12-18 357/week @ 2023-12-25 23/week @ 2024-01-01 244/week @ 2024-01-08 378/week @ 2024-01-15 351/week @ 2024-01-22 442/week @ 2024-01-29 309/week @ 2024-02-05 375/week @ 2024-02-12 691/week @ 2024-02-19 624/week @ 2024-02-26 821/week @ 2024-03-04

2,529 downloads per month
Used in 2 crates (via rust-patch)

MIT/Apache

8KB
166 lines

rust-patch

Patch structs with other structs

Build status Crates.io Documentation

rust-patch allows you to avoid boilerplate code when implementing partial updates of Rust structs.
Simply define a patch struct containing a subset of your fields, derive the Patch trait, and specify the original struct using the #[patch] attribute.
Fields of a patch struct may either be of the same type T as in the original struct or Option<T>.
In the latter case, the field to be patched will be left unchanged if the corresponding field in the patch is None

This crate is no_std compatible.

Container attributes

#[patch = "..."]

Set target struct to be patched

use rust_patch::Patch;
struct Item { data: u32 }

#[derive(Patch)]
#[patch = "Item"]
struct ItemPatch { data: Option<u32> }

Field attributes

By default, any fields in the patch of type Option<T> will be applied as such:

if let Some(val) = patch.field {
    target.field = val;
} 

this behavior can be changed by the following field attributes.

#[patch(as_option)]

The as_option attribute allows patching structs where a field itself is already an Option<T> with the following logic:

if patch.field.is_some() {
    target.field = patch.field;
}

Applying this attribute to a field with a type without an is_some() method results in an error.

#[patch(direct)]

The direct attribute makes it so that the field is treated like any other T, meaning it will be applied like this:

target.field = patch.field;

Applying this attribute to a field where the type is not Option<T> is a no-op.

Example

use rust_patch::Patch;
use serde::Deserialize;

#[derive(PartialEq, Debug)]
struct User {
    id: String,
    name: String,
    email: String,
}

#[derive(Deserialize, Patch)]
#[patch = "User"]
struct UserPatch {
    name: Option<String>,
    email: Option<String>,
}

let user = User {
    id: "6bf25b70-bffa-49e0-905b-2d2e608e3abd".to_string(),
    name: "Max Mustermann".to_string(),
    email: "max.mustermann@example.org".to_string(),
};

let raw_patch = r#"{
    "id": "some invalid id",
    "email": "max.mustermann@example.com"
}"#;

let patch: UserPatch = serde_json::from_str(raw_patch).unwrap();
let patched_user = patch.apply(user);

// Since `id` is not part of our `UserPatch` struct it stays unchanged
assert_eq! {
    patched_user,
    User {
        id: "6bf25b70-bffa-49e0-905b-2d2e608e3abd".to_string(),
        name: "Max Mustermann".to_string(),
        email: "max.mustermann@example.com".to_string()
    }
};

Dependencies

~1.5MB
~35K SLoC