A framework for creating updatable data structures with diff, update, and merge capabilities.
structupdate provides a derive macro and runtime types for building complex data structures
that support:
- Type-safe updates: Modify structures through strongly-typed update operations
- Efficient diffing: Compute minimal differences between two instances
- Merge operations: Combine structures for templating scenarios
- Serialization: Full serde support for all types
Quick Start
use ;
// #[structupdate] only works on types that are Clone + PartialEq + Default
// Create a default instance
let mut config = default;
// Keep a copy of the original value for the sake of the example
let original = config.clone;
// Build an update
let mut update = build_update;
update
.name_set
.settings_set;
// Apply the update
config.apply_update;
// Compute diff between instances
if let Some = original.compute_diff
Values vs Records
This crate distinguishes between values and records:
-
A value is a leaf type (e.g.,
String,u32,bool) that is replaced atomically when updated. Values are wrapped in types like [Value<V>], [OptValue<V>], [ValueList<V>], [ValueSet<K>], or [ValueMap<K, V>]. -
A record is a nested struct that also uses
#[structupdate]and implements the [Node] trait. Records support in-place updates to their fields without replacing the entire struct. Records appear in types like [OptRecord<R>], [RecordMap<K, R>], or directly as struct fields.
This distinction matters for updates: when you update a value, you replace it entirely; when you update a record, you can modify individual fields within it.
Field Types
The macro recognizes these wrapper types for struct fields:
| Type | Description |
|---|---|
[Value<V>] |
A required value with optional custom default |
[OptValue<V>] |
An optional value (can be cleared) |
[OptRecord<R>] |
An optional nested record |
[ValueList<V>] |
An ordered list of values |
[ValueSet<K>] |
A set of unique values |
[ValueMap<K, V>] |
A key-value mapping |
[RecordMap<K, R>] |
A map of nested records |
The #[structupdate] Macro
When applied to a struct, the macro generates:
- A
{StructName}Updatethat represents a set of changes to update aStructNamevalue - A
{StructName}Difftype for representing differences between twoStructNamevalues - Implementation of the [
Node] trait - Builder methods on the
{StructName}Updatetype
Macro Attributes
Struct-level: #[mark(...)]
This is an optional attribute which adds a metadata field to the struct for tracking purposes:
# use structupdate;
# use Value;
Options:
type = T: The type of the mark field (required)serde_skip: Skip the mark field when serializing/deserializing the type. This option is only valid when the type derives theserde::Serializeand/orserde::Deserialize
Field-level: #[structupdate(init_with = ...)]
Specify a custom default value constructor for [Value<V>] fields:
# use structupdate;
# use Value;
Generated Update API
For each field, the macro generates builder methods on the update type:
| Field Type | Generated Methods |
|---|---|
Value<V> |
field_set(v), field_set_to_default() |
OptValue<V> |
field_set(v), field_clear() |
OptRecord<R> |
field_set(r), field_clear(), field_update(u), field_amend(f) |
ValueList<V> |
field_append(v), field_prepend(v), field_pop_last(), etc. |
ValueSet<K> |
field_add(k), field_del(k), field_clear(), field_extend(iter) |
ValueMap<K,V> |
field_set(k, v), field_del(k), field_clear() |
RecordMap<K,R> |
field_amend(k, f), field_try_amend(k, f), field_del(k) |
| Nested struct | field_amend(f) |
The Node Trait
All updatable types implement the [Node] trait, which provides:
apply_update: Apply an update to modify the structurecompute_diff: Calculate differences between instancesapply_diff: Apply a diff to transform the structuremerge: Merge values for templating
Default Value Helpers
The [defaults] module provides helper functions for
creating [Value] instances with custom defaults:
use ;
// These return closures suitable for init_with
let make_name = default_string;
let make_timeout = default_u32;