matchmaker-partial
Support for partial updates and configuration in matchmaker. This crate provides traits and logic for merging partial configuration structures, which is useful for overriding default settings with user-defined values.
Features
- Derive Macros: Automatically generate partial versions of your structs where all fields are wrapped in
Option. - Apply Updates: Easily apply a partial struct to a full struct.
- Dynamic Setting: Update partial structs using string paths and values (ideal for CLI/environment overrides).
- Nested Recursion: Support for recursive partial updates in nested struct hierarchies.
- Merging: Merge multiple partial structs together.
Basic Usage
Using the #[partial] macro to generate a partial version of a struct and applying updates.
use Apply;
use partial;
Dynamic Updates with set
The set method allows updating a partial struct using string paths and values. This requires the #[partial(path)] attribute.
The actual (typed) value is produced from the input list using a custom data deserializer which reads &[String].
use ;
use partial;
[!NOTE] Items in keyed collections can be also referenced either by
path.to.collection.key, or in the provided input value (So that the provided input slice is the concatenation ofList deserializing to key+List deserializing to value).
Nested Structs with recurse
You can use #[partial(recurse)] to handle nested structures.
use Apply;
use partial;
Collections
When #[partial(unwrap)]is applied to a collection (HashMap, Vec, BTreeMap, BTreeSet), the corresponding field omits the wrapping Option. This holds even for collections wrapped in Option.
When #[partial(recurse)] is applied to a collection, the nesting propogates to the internal type: Vec<Inner> becomes Vec<PartialInner>.
Otherwise if the mirror collection type is wrapped in Option, apply overwrites. If the inner type is also partial, corresponding values are applied to, and any extra values are applied to the default prototype and inserted. Otherwise, the base data is extended.
The behavior is summarized in the following table:
Type Transformation
| Original | No Recurse / Not Unwrapped | No Recurse / Unwrap | Recurse / Not Unwrapped | Recurse / Unwrap |
|---|---|---|---|---|
Vec<T> |
Option<Vec<T>> |
Vec<T> |
Option<Vec<P>> |
Vec<P> |
Option<Vec<T>> |
Option<Vec<T>> |
Vec<T> |
Option<Vec<P>> |
Vec<P> |
| Apply behavior | Overwrite | Extend | Apply, then extend from upgraded versions | Upgrade all to T, then extend |
Set Attributes
set = "sequence"
On a collection, using #[partial(set = "sequence")] causes it to deserialize input as a sequence rather than as the next given single value.
let mut partial = default;
partial.set.unwrap;
assert_eq!;
set = "recurse"
On a collection, using #[partial(set = "recurse")] adds support for additional path segments after its own. The additional path segments are used to create a new partial item with a single field set, which is then appended to the collection.
let mut partial = default;
partial.set.unwrap;
assert_eq!;
serde(alias)
Fields with #[serde(alias = "...")] or #[partial(alias = "...")] can be updated using any of the specified aliases in addition to the original field name.
let mut partial = default;
partial.set.unwrap;
assert_eq!;
serde(flatten)
Flattened fields allow embedding nested structs directly at the top level. When a flattened field also uses #[partial(recurse)], set delegates updates to the nested partial rather than expecting a top-level field match.
#[partial(flatten)] is also supported.
let mut partial = default;
partial.set.unwrap;
partial.set.unwrap;
assert_eq!;
assert_eq!;
Modify with merge
Adding #[partial(merge)] generates the Merge trait for the partial struct, providing:
merge(&mut self, other): Updates fields from another partial where they areSome.clear(&mut self): Resets all fields toNone.
Example
let mut hero = Character ;
let mut p1 = default;
p1.name = Some;
let mut p2 = default;
p2.stats.hp = Some;
p1.merge;
hero.apply;
assert_eq!;
assert_eq!;
assert_eq!;
let mut p3 = default;
p3.name = Some;
p3.clear;
assert_eq!;
Deserializer
Set fills values from &[String] by using a simple data deserializer.
- Most primitive types read a single word.
- Tuples and Sequences attempt to deserialize their next values from the remaining words sequentially.
- Maps deserialize keys and values alternately in sequence.
- Struct deserialization ends when the next word is not a field name.
- Tuple deserialization ends when the requisite number of values have been deserialized.
- Otherwise, maps and sequence types consume the input until exhausted.
- Options are transparent to their inner type unless the word list is exhausted, or the following word is "null". (This last behavior may be subject to change).
- Unit structs expect "" or "()".