derive-deftly 1.11.0

An ergonomic way to write derive() macros
Documentation
<!-- @dd-navbar -->
<!-- this line automatically maintained by update-navbars --><nav style="text-align: right; margin-bottom: 12px;">[ <em>docs: <a href="https://docs.rs/derive-deftly/latest/derive_deftly/index.html">crate top-level</a> | <a href="https://docs.rs/derive-deftly/latest/derive_deftly/index.html#overall-toc">overall toc, macros</a> | <a href="https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html">template etc. reference</a> | <a href="https://diziet.pages.torproject.net/rust-derive-deftly/latest/guide/"><strong>guide/tutorial</strong></a></em> ]</nav>

# A better solution to our problems with Diff

In previous sections, we've seen
[a few problems](difference-problems.md)
with writing a template that can define either a struct or an enum.
We built a [brute-force solution](difference-if-enum.md)
that was workable if not especially elegant.
Here, we'll show a better way.

We'll start by revisiting those problems;
this time we'll show a better solution to each one.
After we've gone over those solutions,
we'll put them together into a working template.

## Revisiting our problems

So, how can we make our template?

### Solving problem 1: `$tdefkwd` for a top-level keyword

We needed [a way to get the right keyword](difference-problems.md#kwd)
for our new type.
This is available nicely as [`$tdefkwd`][x:tdefkwd]
("toplevel definition keyword"),
which expands to `struct`, `union`, or `enum` as appropriate.


### Solving problem 2: `$tdefgens` for generics with defaults

We needed a way to
[declare generics along with their defaults](difference-problems.md#gen-defaults).
This come in the form of [`$tdefgens`][x:tdefgens],
which gives the generics from the top-level type
along with their bounds
_and_ their defaults.

### Solving problem 3: `$tdefvariants` to brace our variants as needed

We needed some way to
[wrap our enum variants in braces](difference-problems.md#enum-braces),
but leave our structs unwrapped.

derive-deftly solves this problem with
the [`$tdefvariants`][x:tdefvariants] expansion,
which adds braces only when the top-level type is an enum.
In other words, `${tdefvariants ARG}` expands to `{ARG}` for an enum,
but `ARG` otherwise.

### Solving problem 4: `$vdefbody` and `$fdefine` for struct/variant types

We needed some way to make
[appropriate type and field declarations](difference-problems.md#defining)
no matter whether the current struct or variant
is a unit struct/variant, a tuple struct/variant,
or a struct with named fields/fields.

We solve this problem in two parts.

First, the [`$vdefbody`][x:vdefbody] expansion gives us the
appropriate syntax to declare a variant or structure
corresponding to the current variant/structure,
with the necessary braces, parentheses, and/or trailing semicolon or comma.

Specifically, `${vdefbody VNAME FIELDS}` will expand to one of:

 * `FIELDS;` for unit structs (though typically FIELDS will be empty)
 * `(FIELDS);` for tuple structs
 * `{FIELDS}` for structs with named fields
 * `VNAME FIELDS,` for a unit variant in an enum
   (though typically FIELDS will be empty)
 * `VNAME(FIELDS),` for a tuple variant in an enum
 * `VNAME { FIELDS },` for a variant with named fields in an enum.

Second,
to provide a field name and colon if appropriate, we use
[`${fdefine NAME}`][x:fdefine],
which expands to `NAME:` if fields are named,
and to nothing otherwise.

### Solving problem 5: `$fdefvis` for visibility

We needed some way to define
[appropriate visibility](difference-problems.md#fvis-difference)
for struct fields,
given that `pub` isn't allowed to appear in an enum definition.

For this, we use [`$fdefvis`][x:fdefvis],
which expands to the visibility of the current field in a struct,
and to nothing in an enum.


## Putting it all together

This probably sounds like a lot,
and admittedly it can be tricky to synthesize.

As a template, it's handy to start by modifying this syntax,
which just reproduces the original type with a new name.
```rust,ignore
$tvis $tdefkwd $<$tname Copy><$tdefgens>
${tdefvariants $(
    ${vdefbody $vname $(
        $fdefvis ${fdefine $fname} $ftype,
    ) }
) }
```

Based on that, we produce the following.
(As an added bonus, we'll define the actual `diff` method!)

<!-- TODO: When I use $crate:: below, the rustdoc doesn't pass its tests. -->
<!-- We should investigate. -->

```rust
# pub trait Diff { type Difference: std::fmt::Debug; fn diff(&self, o:&Self) -> Option<Self::Difference>; }
# impl Diff for u32 { type Difference = (); fn diff(&self,o:&Self) -> Option<()> {None} }
# use derive_deftly::{define_derive_deftly, Deftly};
define_derive_deftly!{
    Diff:

    ${if is_union {${error "Unions aren't supported"}}}

    ${define DIFF_TYPE
        { Option< <$ftype as Diff>:: Difference> }
    }

    #[derive(Debug)]
    $tvis $tdefkwd $<$tname Diff><$tdefgens>
    ${tdefvariants
        $(
            ${vdefbody $<Both $vname>
                $(
                    $fdefvis ${fdefine $fname} $DIFF_TYPE ,
                )
            }
        )
        ${if is_enum {
            VariantChanged {
                original_value: $ttype,
                new_value: $ttype,
            },
        }}
    }

    impl<$tgens> $<$ttype Diff> where $twheres {
        /// Helper: Return this value if it reflects a real change.
        fn if_is_a_change(self) -> Option<Self> {
            match &self {
                $(
                    // (1)
                    ${vpat self=$<$ttype Diff> vname=$<Both $vname>} => {
                        if $( $fpatname.is_none() && ) true  {
                            return None;
                        }
                    }
                )
                ${if is_enum { Self::VariantChanged{..} => {} }}
            }
            return Some(self);
        }
    }

    impl<$tgens> Diff for $ttype where
        ${if is_enum {$ttype: Clone,}}
        $twheres
    {
        type Difference = $<$ttype Diff>;
        fn diff(&self, other: &Self) -> Option<Self::Difference> {
            match (self, other) {
                $(
                    // (2)
                    ($vpat, ${vpat fprefix=other_}) => {
                        // (3)
                        ${vtype self=$<$ttype Diff> vname=$<Both $vname>} {
                            $(
                                $fname : Diff::diff($fpatname, $<other_ $fname>),
                            )
                        }
                    }
                ) // End variants.
                ${if is_enum {
                    (original_value, new_value) => {
                        Self::Difference::VariantChanged {
                            original_value: original_value.clone(),
                            new_value: new_value.clone(),
                        }
                    }
                }}
            }.if_is_a_change()
        }
    }
}
# use derive_deftly_template_Diff;
# #[derive(Clone,Debug,Deftly)]
# #[derive_deftly(Diff)]
# struct Unit;
# #[derive(Clone,Debug,Deftly)]
# #[derive_deftly(Diff)]
# struct Tuple(u32, u32);
# #[derive(Clone,Debug,Deftly)]
# #[derive_deftly(Diff)]
# struct Named { a: u32, b: u32 }
# #[derive(Clone,Debug,Deftly)]
# #[derive_deftly(Diff)]
# enum Enum { Un, Tup(u32,u32), Nam { a: u32, b: u32 } }
```

With understanding,
this approach should appear much terser and more logical
than the many-cases approach from the
[last section](difference-if-enum.md).

We've introduced a few previously unseen expansions:
let's talk about them now.

At the beginning, we use [`$error`][x:error].
If it is ever expanded,
then the whole template expansion is rejected with a compile-time error.
We use it to make sure that nobody tries to apply our template
to a `union`.

We use [`$vpat`][x:vpat] in a new form.
As discussed [in the reference][x:vpat],
`$vpat` can take arguments to change its behavior.
We use this feature twice:

- At `// (1)`
  we use the `self` argument
  to make our pattern destructure the Diff object
  rather than the original top-level type,
  and we use `vname` to destructure a `$<Both $vname>` variant
  rather than the original variant name.
- At `// (2)`,
  we use the `fprefix` argument to give the second instance of our pattern
  a different set of binding names,
  so they will not conflict with the first instance.

Finally, we pass arguments to [`$vtype`][x:vtype] at `// (3)`,
so that instead of constructing an instance of the current variant,
it constructs the `*Diff` type with an appropriate `Both*` name.


[x:tdefkwd]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:tdefkwd
[x:tdefvariants]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:tdefvariants
[x:vdefbody]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:vdefbody
[x:fdefine]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:fdefine
[x:fdefvis]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:fdefvis
[x:tdefgens]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:tdefgens
[x:error]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:error
[x:vpat]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:vpat
[x:vtype]: https://docs.rs/derive-deftly/latest/derive_deftly/doc_reference/index.html#x:vtype