Getting started with if/then/else conditionals
In the example above,
we made it possible to rename the "new" function
generated by our template.
But our approach is flawed:
if the user doesn't provide
the #[deftly(newfn)]
attribute,
the template won't make a function name at all!
Let's show how to fix that:
#![allow(unused)] fn main() { use derive_deftly::define_derive_deftly; define_derive_deftly! { Constructor for struct: impl<$tgens> $ttype where $twheres { $tvis fn // (1) This "if" defines the function name: ${if tmeta(constructor(newfn)) { ${tmeta(constructor(newfn)) as ident} } else { new } } // The rest is as before: ( $( $fname: $ftype , ) ) -> Self { Self { $( $fname , ) } } } } use derive_deftly::Deftly; #[derive(Deftly)] #[derive_deftly(Constructor)] #[deftly(constructor(newfn="construct_example"))] struct Example { a: f64, b: String } #[derive(Deftly)] #[derive_deftly(Constructor)] struct Example2 { a: f64, b: String } }
Have a look at the part of the template marked with // (1)
.
It introduces a new concept: conditional expansion.
The
${if cond {expansion}}
keyword checks whether a given condition is true.
If it is, then the $if
keyword expands to the expansion second argument.
Otherwise, it expands to an "else" argument (if any).
Also, you can chain
$if
s, as in${if COND1 { ... } else if COND2 { ... } else { ... }
and so on!
Here, the condition is
tmeta(constructor(newfn))
("toplevel metavalue").
That condition is true if the current type
has an #[deftly(newfn)]
attribute,
and false otherwise.
There are also vmeta
and fmeta
conditions
to detect #[deftly(..)]
attributes
on variants and fields respectively.
Note: Don't confuse conditions with expansions! As we use it here,
tmeta(x)
is a condition (true or false), whereas${tmeta(x) as ident}
is an expansion.Even though the names are similar in this case, conditions and expansions have separate namespaces; you cannot (in general) use them interchangeably.
There is a complete list of recognized conditions in the reference.
Multiple exclusive options with $select1
Sometimes you want to make sure exactly one condition matches.
This is a good choice when you have multiple options for controlling some feature, and you want to make sure that the user has specified exactly one (or maybe, no more than one).
You can do this using the ${select1}
keyword:
${select1 COND1 { ... } else if COND2 { ... } ... else { ... }
Its syntax is nearly the same as ${if}
,
except that all conditions are checked.
If more than one condition is true,
then derive-deftly rejects ${select1}
:
it fails to expand, and
emits an error.
If no conditions are true,
and there is no else
clause,
then ${select1}
is also rejected
(it fails to expand,
and emits an error).
Here's an example of how we could use ${select1}
if we wanted to allow constructor(default_name)
,
as a synonym for our default behavior.
In this case, we enforce that the user has specified
constructor(newfn(X))
or constructor(default_name)
or neither,
but not both:
#![allow(unused)] fn main() { use derive_deftly::define_derive_deftly; define_derive_deftly! { Constructor for struct: impl<$tgens> $ttype where $twheres { pub fn ${select1 tmeta(constructor(newfn)) { ${tmeta(constructor(newfn))} } else if tmeta(constructor(default_name)) { new } else { new } } () {} } } }