Optifier
Proc macro crate: Partial derive
[!note] This crate is early development phase. I use it only in my personal projects and update it as needed.
Derive Partial on a struct Foo to generate a new struct named FooPartial
where every field type is wrapped in Option<T> unless it is already an Option<T>.
Only structs with named fields are accepted; tuple and unit structs are not supported yet.
For every FooPartial the macro also generates:
- an inherent
mergemethod to combine two partial values, and - a
TryFrom<FooPartial> for Fooimplementation with a dedicated error type.
Example:
use optifier;
expands to:
// merge two partials (right-hand side only fills in missing values)
// fallible conversion back to the original type
// (succeeds only if all non-optional original fields are present)
merge method on *Partial types
For every generated <OriginalName>Partial the macro adds a merge method:
Semantics:
- It returns a new partial where, for each field, the value from
selfis used if it isSome. - If
self.fieldisNone, the value fromother.fieldis used. - This is a shallow merge; it does not recurse into nested structs.
This is useful for layering configurations or patches:
let base: FooPartial = /* ... */;
let override_: FooPartial = /* ... */;
let merged = base.merge;
Fallible conversion: TryFrom<*Partial> for Original
For each original struct Foo, the macro generates:
-
A dedicated error type:
<OriginalName>PartialError(e.g.FooPartialError). -
An implementation of
TryFrom<FooPartial> for Foo:- The conversion succeeds only if every field that was non-
OptioninFooisSomeinFooPartial. - Fields that were already
Option<...>inFooare allowed to beNonewithout causing an error. - The error type is an
enumannotated withthiserror::Error, with one variant per non-optional field in the original struct. - Variant naming:
- Field name is converted to
PascalCaseusing theconvert_casecrate. - The suffix
Missingis appended. - Examples:
a→AMissinguser_id→UserIdMissing
- Field name is converted to
- The conversion succeeds only if every field that was non-
The error variants have messages of the form: "<field_name> field is missing".
Concrete example:
Generates something equivalent to:
You can then use the standard try_into API:
let partial: UserPartial = /* ... */;
let user: User = partial.try_into?; // or User::try_from(partial)?
#[optifier::partial_derive(...)] attribute
The #[optifier::partial_derive(...)] attribute configures which traits are derived
for the generated *Partial type.
- It is applied to the original struct, together with
#[derive(optifier::Partial)]. - It accepts a comma-separated list of trait paths (e.g.
Debug,Clone,PartialEq, or fully qualified paths). - Whatever traits you list there are emitted as a
#[derive(...)]on the generated<OriginalName>Partialtype.
Example:
use optifier;
generates roughly: