Repository navigation
feat: magic_map_leaves! — declare a crate's leaves once, replay them … - #3
Merged
Merged
Conversation
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…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!toString— 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_leafcovers 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: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 amacro_rules!cannot express the inner$, whilequote!interpolates on#and passes$through untouched. Two macro_rules limits ruled out the alternatives — apathmetavariable can be followed by neither::nor!, so the published list is invoked by ident and emits its impls directly rather than taking a callback.customexists because a macro cannot see animplblock: expansion observes only its own tokens, so a hand-writtenMapFromhas 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_provideris 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.