Skip to content

feat: magic_map_leaves! — declare a crate's leaves once, replay them … - #3

Merged
rsansores merged 1 commit into
mainfrom
feat/leaf-registry
Aug 9, 2026
Merged

rsansores merged 1 commit into
mainfrom
feat/leaf-registry

Conversation

@rsansores

Copy link
Copy Markdown
Owner

…everywhere

Fixes the design shipped in 0.3.0, which was wrong. The two-tier probe left the destination type open, so probing matched tier 1 on the source alone and unified the destination to whatever that impl produced: any type with both a declared foreign→foreign mapping and a leaf conversion elsewhere — an enum with a DTO twin and a map_display! to String — silently resolved to the wrong one, and the expected type did not override it. Integration caught it, not the unit tests; shared_source_reaches_both_its_declared_pair_and_its_leaf covers it now.

So the local trait is one funnel again, with every leaf delegated in — no ambiguity is possible when each pair is a distinct impl. What made that unpalatable was the bookkeeping: every consumer restating every leaf. magic_map_leaves! removes it. A crate declares its leaves once, in its root, and consumers name the CRATE:

// leaf-owning crate
magic_map_leaves! {
    identity: [crate::enums::Species],
    display:  [crate::enums::Species],
    parse:    [crate::enums::Species],
    custom:   [crate::wire::Fahrenheit => String],
}

// consumer
magic_map_scope!(from: [my_db, my_commons]);

Adding an enum to that block reaches every consumer with no edit on their side. Own-crate paths are written crate::… and republished as $crate:: so a consumer resolves them against the declaring crate.

It is a proc macro for the same reason the schema derive is: generating a macro_rules! from a macro_rules! cannot express the inner $, while quote! interpolates on # and passes $ through untouched. Two macro_rules limits ruled out the alternatives — a path metavariable can be followed by neither :: nor !, so the published list is invoked by ident and emits its impls directly rather than taking a callback.

custom exists because a macro cannot see an impl block: expansion observes only its own tokens, so a hand-written MapFrom has to be named somewhere. The impl stays where it is; only the pair is registered.

Verified on a workspace with 38 leaf declarations across three crates: its service layer went from 28 errors to none, with from: [commons, db, rpc] and no type transcribed. leaf_provider is a new dev-dependency crate so the cross-crate half is exercised for real — declaring and replaying inside one crate would prove nothing, since the point is that a consumer resolves the paths.

…everywhere

Fixes the design shipped in 0.3.0, which was wrong. The two-tier probe left the
destination type open, so probing matched tier 1 on the source alone and
unified the destination to whatever that impl produced: any type with both a
declared foreign→foreign mapping and a leaf conversion elsewhere — an enum with
a DTO twin *and* a `map_display!` to `String` — silently resolved to the wrong
one, and the expected type did not override it. Integration caught it, not the
unit tests; `shared_source_reaches_both_its_declared_pair_and_its_leaf` covers
it now.

So the local trait is one funnel again, with every leaf delegated in — no
ambiguity is possible when each pair is a distinct impl. What made that
unpalatable was the bookkeeping: every consumer restating every leaf.
`magic_map_leaves!` removes it. A crate declares its leaves once, in its root,
and consumers name the CRATE:

    // leaf-owning crate
    magic_map_leaves! {
        identity: [crate::enums::Species],
        display:  [crate::enums::Species],
        parse:    [crate::enums::Species],
        custom:   [crate::wire::Fahrenheit => String],
    }

    // consumer
    magic_map_scope!(from: [my_db, my_commons]);

Adding an enum to that block reaches every consumer with no edit on their side.
Own-crate paths are written `crate::…` and republished as `$crate::` so a
consumer resolves them against the declaring crate.

It is a proc macro for the same reason the schema derive is: generating a
`macro_rules!` from a `macro_rules!` cannot express the inner `$`, while
`quote!` interpolates on `#` and passes `$` through untouched. Two macro_rules
limits ruled out the alternatives — a `path` metavariable can be followed by
neither `::` nor `!`, so the published list is invoked by ident and emits its
impls directly rather than taking a callback.

`custom` exists because a macro cannot see an `impl` block: expansion observes
only its own tokens, so a hand-written `MapFrom` has to be named somewhere. The
impl stays where it is; only the pair is registered.

Verified on a workspace with 38 leaf declarations across three crates: its
service layer went from 28 errors to none, with `from: [commons, db, rpc]` and
no type transcribed. `leaf_provider` is a new dev-dependency crate so the
cross-crate half is exercised for real — declaring and replaying inside one
crate would prove nothing, since the point is that a consumer resolves the
paths.
@rsansores
rsansores merged commit 6ad75a7 into main Aug 9, 2026
1 check passed
@rsansores
rsansores deleted the feat/leaf-registry branch August 9, 2026 22:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant