Background
I have a crate https://crates.io/crates/magic_migrate that provides a trait TryMigrate
. I’m working on writing a macro that allows someone to define this trait by providing an error and a “chain” of structs that define a migration order. Eventually I would like to make an even-more generic macro that takes three things (error, deserializer, and struct chain).
Due to taking multiple different kinds of inputs, I would like it to have a keyword arg like syntax. For example:
try_migrate_toml_chain!(
error: PersonMigrationError,
chain: [PersonV1, PersonV2],
);
The issue I’m running into is that I have to re-define my macro to be position independent. Here’s the code I came up with that works, but has duplication:
#[macro_export]
macro_rules! try_migrate_toml_chain {
// Base case
(error: $err:ident, chain: [$a:ident] $(,)?) => {
impl TryMigrate for $a {
type TryFrom = Self;
type Error = $err;
fn deserializer<'de>(input: &str) -> impl Deserializer<'de> {
toml::Deserializer::new(input)
}
}
impl From<Infallible> for $err {
fn from(_value: Infallible) -> Self {
unreachable!()
}
}
};
// Position variant
(chain: [$a:ident], error: $err:ident $(,)?) => {
$crate::try_migrate_toml_chain!(error: $err, chain: [$a]);
};
// Rest case
(error: $err:ident, chain: [$a:ident, $($rest:ident),+] $(,)?) => (
// Call the base case to link A => A
$crate::try_migrate_toml_chain!(error: $err, chain: [$a]);
// Link the rest i.e. A => B, B => C, etc.
$crate::try_migrate_link!($a, $($rest),+);
);
// Position variant
(chain: [$a:ident, $($rest:ident),+], error: $err:ident $(,)?) => (
$crate::try_migrate_toml_chain!(error: $err, chain: [$a, $($rest),+])
);
}
This works, but is quite verbose. It gets even more verbose with 3 arguments. This leads me to my question.
Question
Is there an easy(er) to define a macro so the keyword-ish inputs can be position independent than having to manually re-define the macro_rules! pattern with a different order?