smithy-lang/smithy

Selectors: recursive mixin check?

Closed this issue · 3 comments

Hi! I was wondering if there's a way to write a selector that checks if the given shape has a mixin... but checking recursively.

For example, consider this model:

$version: "2"

namespace demo

@mixin
structure MyMixin {}

@hasMyMixin
@mixin
structure Direct with [MyMixin] {}

// structure Indirect with [Direct] {}

@trait(selector: ":test(-[mixin]-> structure [id = 'demo#MyMixin'])")
structure hasMyMixin {}

In its current state, it's valid. However, if you uncomment Indirect it no longer matches (it inherits the hasMyMixin trait, but that fails to validate on Indirect due to the mixin being transitive).

Would it make sense to expand the mixin relationship, or provide a recursive variant of -[...]->? Perhaps a "forward directed recursive neighbor" syntax, like -[...]~>?

Is there a better, existing way to do what I'm looking for? (without resorting to Java)

Maybe we could add a synthetic “transitiveMixin” relationship available to selectors which includes direct and transitive mixins. Or maybe a :hasMixin(a, c, c) function that returns true if all mixins are present on a shape. I like the relationship but we’d need to see how disruptive it is vs a function.

I pushed up a branch that has a proof of concept for :recursive(expr) function in it. Any result that hasn't been seen yet is recursively selected by the given expression, so you can do :recursive(-[mixin]->) to get all mixins of a shape. It has no tests or docs, and I still need to deeply think through how good of an idea this specific approach is.

If you wanna try it out, you could pull this down and then build the Smithy CLI using ./gradlew :smithy-cli:runtime. I want to make sure there are more use cases than just finding transitive mixins. Edit: I'll try to list some ideas on what this could do:

  1. Find direct and transitive mixins: :recursive(-[mixin]->).
  2. Find out if a shape has a mixin directly or transitively: :test(:recursive(-[mixin]->) [id=test#MyMixin])
  3. Find the enclosing resource hierarchy of a shape: :recursive(<-[resource, service]-)
  4. Find out if a shape is in the closure of a specific resource: :test(:recursive(<-[resource]-) [id=test#A])

0dfdc54

Thank you!