Using attributes to make a template take arguments

Let's suppose we want to make our Constructor template a little more flexible: we'd like to be able to give the new function a different name.

The usual way to pass an option to a derive macro is with an attribute, something like this:

#[derive(Deftly)]
#[derive_deftly(Constructor)]
#[deftly(constructor(newfn="function_name"))]
struct Example(...);

We can extend our template to take a function name, like this:

#![allow(unused)]
fn main() {
use derive_deftly::define_derive_deftly;
define_derive_deftly! {
   Constructor for struct:

   impl<$tgens> $ttype where $twheres {
      $tvis fn ${tmeta(constructor(newfn)) as ident} // (1)
      ( $( $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
}
}

Here, instead of specifying "new" for the method name in our template, we give the name as ${tmeta(constructor(newfn))} as ident. This tells the template to look for an #[deftly(constructor(newfn="..."))] attribute on the type, to interpret that attribute as an identifier, and to use the value of that attribute in place of the keyword.

The as ident portion is mandatory; without it, derive-deftly can't tell how to parse the argument to the argument. Instead of ident, you can describe other kinds syntactical types, such as str, ty, path, or expr. See the reference for a complete list.

Note that we explicitly named our attribute as constructor(newfn). We could instead have specified it as newfn, but that would have the potential to conflict with attributes interpreted by other templates. As a best practice, if we expect our template to be widely used, we should namespace our attributes as in the example above.

The $tmeta ("toplevel meta") keyword that we used here tells the template to look at the #[deftly] attributes for the type. We can, instead, use $vmeta ("variant meta") to look for #[deftly] attributes for the current variant, or $fmeta ("field meta") to to look for #[deftly] attributes for the current field.

(We'll see an example of using $fmeta in a little while.)