You are intermixing the syntactic levels of the trait names and the generic type variables.
In the matcher, $trait and $tyvar are independent. They are both nested in 1 and 2 layers of repetition, respectively, inside the root, but not inside each other.
In the substitution part, however, you are trying to put $tyvarinside the repetition for $trait – which causes $tyvar be inside one too many layers of repetition.
note the macro entry point is slight different from your original example code:
use vis capture to optionally capture the pub keyword
use + repeat instead of * repeat for $trait name captures
use * repeat instead of ? optional for struct $body definition
omit the optional ? $tyvar captures for the recursion entries: an empty <> pair is equivilent to no angled brackets
also, technically the $trait capture should use $trait:path instead of $trait:ident, but that brings a little bit complication for the recursion patttern, which doesn't help illustrate the technique here (a path catpure can't be directly followed by tt capture, to avoid parsing ambiguouty, it can be done, that unnecessarily make the example more complicated).