struct-patch 0.13.0

A library that helps you implement partial updates for your structs.
Documentation
use struct_patch::Patch;

fn log_field(field: &str) {
    println!("[default_log] field changed: {field}");
}

#[derive(Default, Patch)]
#[patch(attribute(derive(Debug, Default)))]
#[patch(default_log(log_field))]
struct Config {
    host: String,
    port: u16,
    debug: bool,
}

// Generated by Patch derive macro
//
// #[derive(Debug, Default)]
// struct ConfigPatch {
//     host: Option<String>,
//     port: Option<u16>,
//     debug: Option<bool>,
// }
//
// With the `box` feature enabled, the following impl is also generated:
//
// impl Patch<Box<ConfigPatch>> for Config { ... }

fn main() {
    // --- apply a boxed patch ---

    let mut config = Config::default();

    let boxed: Box<ConfigPatch> = Box::new(ConfigPatch {
        host: Some("localhost".into()),
        port: Some(8080),
        debug: None,
    });

    config.apply(boxed);

    assert_eq!(config.host, "localhost");
    assert_eq!(config.port, 8080);
    assert!(!config.debug);

    // --- apply_with_log also works through Box ---

    let mut patched_fields = Vec::new();
    config.apply_with_log(
        Box::new(ConfigPatch {
            host: None,
            port: None,
            debug: Some(true),
        }),
        |field| patched_fields.push(field.to_string()),
    );

    assert!(config.debug);
    assert_eq!(patched_fields, vec!["debug"]);

    // --- into_patch and into_patch_by_diff return Box<ConfigPatch> ---

    let snapshot = Config {
        host: "localhost".into(),
        port: 8080,
        debug: false,
    };
    let current = Config {
        host: "localhost".into(),
        port: 9090,
        debug: true,
    };

    let diff: Box<ConfigPatch> = current.into_patch_by_diff(snapshot);

    assert_eq!(diff.host, None);
    assert_eq!(diff.port, Some(9090));
    assert_eq!(diff.debug, Some(true));
}