Skip to content

Commit eb88b61

Browse files
committed
Add recursive neighbor selector
1 parent e8d364b commit eb88b61

File tree

6 files changed

+250
-116
lines changed

6 files changed

+250
-116
lines changed

docs/source/spec/core/selectors.rst

Lines changed: 59 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Shapes can be matched by type using the following tokens:
5555

5656
The following selector matches all string shapes in a model:
5757

58-
::
58+
.. code-block:: none
5959
6060
string
6161
@@ -96,7 +96,7 @@ We can match shapes based on traits using an *attribute selector*. The
9696
following selector finds all structure shapes with the :ref:`error-trait`
9797
trait:
9898

99-
::
99+
.. code-block:: none
100100
101101
structure[trait|error]
102102
@@ -106,21 +106,21 @@ and that that specific trait is ``time``.
106106

107107
We can match string shapes that have a specific trait value:
108108

109-
::
109+
.. code-block:: none
110110
111111
structure[trait|error=client]
112112
113113
Matching on trait values only works for traits that have a scalar value
114114
(e.g., strings, numbers, and booleans). We can also match case-insensitvely
115115
on the value by appending " i" before the closing bracket:
116116

117-
::
117+
.. code-block:: none
118118
119119
structure[trait|error=CLIENT i]
120120
121121
Fully-qualified trait names are also supported:
122122

123-
::
123+
.. code-block:: none
124124
125125
string[trait|smithy.example#customTrait=foo]
126126
@@ -132,7 +132,7 @@ Attribute selectors can be used to match the :ref:`shape ID <shape-id>`. The
132132
following example matches a single resource shape with an ID of
133133
``smithy.example#Foo``:
134134

135-
::
135+
.. code-block:: none
136136
137137
resource[id='smithy.example#Foo']
138138
@@ -143,28 +143,28 @@ Smithy provides several attributes in the ``id`` namespace to make matching
143143
on a shape ID easier. The following example finds all shapes that are in the
144144
"smithy.example" namespace:
145145

146-
::
146+
.. code-block:: none
147147
148148
resource[id|namespace=smithy.example]
149149
150150
Though not as clear, matching shapes in a specific namespace can also be
151151
achieved using the ``^=`` comparator against ``id``:
152152

153-
::
153+
.. code-block:: none
154154
155155
resource[id^=smithy.example#]
156156
157157
The following example matches all member shapes that have a member name of
158158
"key":
159159

160-
::
160+
.. code-block:: none
161161
162162
resource[id|member=key]
163163
164164
Though not as clear, matching members with a member name of "key" can also be
165165
achieved using the ``$=`` comparator against ``id``:
166166

167-
::
167+
.. code-block:: none
168168
169169
resource[id$="$key"]
170170
@@ -210,21 +210,21 @@ The *current* shape evaluated by a selector is changed using a neighbor token,
210210
shape. For example, the following selector returns the key and value members of
211211
every map:
212212

213-
::
213+
.. code-block:: none
214214
215215
map > member
216216
217217
We can return just the key members or just the value members by adding an
218218
attribute selector on the ``id|member``:
219219

220-
::
220+
.. code-block:: none
221221
222222
map > member[id|member=key]
223223
224224
Neighbors can be chained to traverse further into a shape. The following
225225
selector returns strings that are targeted by list members:
226226

227-
::
227+
.. code-block:: none
228228
229229
list > member > string
230230
@@ -237,7 +237,7 @@ directed edge traversal is necessary to match the appropriate shapes. For
237237
example, the following selector returns the "bound", "input", "output",
238238
and "errors" relationships of each operation:
239239

240-
::
240+
.. code-block:: none
241241
242242
operation > *
243243
@@ -246,15 +246,15 @@ by a comma separated list of :ref:`relationships <selector-relationships>`,
246246
followed by ``]->``. The following selector matches all structure
247247
shapes referenced as operation input or output.
248248

249-
::
249+
.. code-block:: none
250250
251251
operation -[input, output]->
252252
253253
The ``:test`` function can be used to check if a shape has a named
254254
relationship. The following selector matches all resource shapes that define
255255
an identifier:
256256

257-
::
257+
.. code-block:: none
258258
259259
resource:test(-[identifier]->)
260260
@@ -265,11 +265,33 @@ explicitly requested using a ``trait`` directed relationship. The following
265265
selector finds all service shapes that have a protocol trait applied to it
266266
(that is, a trait that is marked with the :ref:`protocolDefinition-trait`):
267267

268-
::
268+
.. code-block:: none
269269
270270
service:test(-[trait]-> [trait|protocolDefinition])
271271
272272
273+
Recursive neighbors
274+
~~~~~~~~~~~~~~~~~~~
275+
276+
The ``~>`` neighbor selector finds all shapes that are recursively connected in
277+
the closure of another shape.
278+
279+
The following selector finds all operations that are connected to a service
280+
shape:
281+
282+
.. code-block:: none
283+
284+
service ~> operation
285+
286+
The following selector finds all operations that do not have the :ref:`http-trait`
287+
that are in the closure of a service marked with the ``aws.protocols#restJson``
288+
trait:
289+
290+
.. code-block:: none
291+
292+
service[trait|aws.protocols#restJson1] ~> operation:not([trait|http])
293+
294+
273295
.. _selector-relationships:
274296

275297
Relationships
@@ -394,34 +416,34 @@ comma (",").
394416

395417
The following selector matches all string and number shapes:
396418

397-
::
419+
.. code-block:: none
398420
399421
:each(string, number)
400422
401423
Each can be used inside of neighbors too. The following selector
402424
matches all members that target a string or number:
403425

404-
::
426+
.. code-block:: none
405427
406428
member > :each(string, number)
407429
408430
The following ``:each`` selector matches all shapes that are either
409431
targeted by a list member or targeted by a map member:
410432

411-
::
433+
.. code-block:: none
412434
413435
:each(list > member > *, map > member > *)
414436
415437
The following selector matches all list and map shapes that target strings:
416438

417-
::
439+
.. code-block:: none
418440
419441
:each(:test(list > member > string), :test(map > member > string))
420442
421443
Because none of the selectors in the ``:each`` function are intended to
422444
change the current node, this can be reduced to the following selector:
423445

424-
::
446+
.. code-block:: none
425447
426448
:test(:each(list > member > string, map > member > string))
427449
@@ -435,15 +457,15 @@ shape.
435457

436458
The following selector is used to match all string and number shapes:
437459

438-
::
460+
.. code-block:: none
439461
440462
:test(string, number)
441463
442464
The ``:test`` function is much more interesting when used to test if a shape
443465
contains a neighbor in addition to other filtering. The following example
444466
matches all shapes that are bound to a resource and have no documentation:
445467

446-
::
468+
.. code-block:: none
447469
448470
:test(-[bound, resource]->) :not([trait|documentation])
449471
@@ -457,20 +479,20 @@ filtered out from the result set.
457479

458480
The following selector matches every shape except strings:
459481

460-
::
482+
.. code-block:: none
461483
462484
:not(string)
463485
464486
The following selector matches every shape except strings and floats:
465487

466-
::
488+
.. code-block:: none
467489
468490
:not(string, float)
469491
470492
The following example matches all shapes except for strings that are targeted
471493
by a list member:
472494

473-
::
495+
.. code-block:: none
474496
475497
:not(list > member > string)
476498
@@ -482,15 +504,15 @@ The ``:test`` function can be used to test a shape, potentially traversing its
482504
neighbors, without changing the return value of the test. The following
483505
example does not match any list shape that has a string member:
484506

485-
::
507+
.. code-block:: none
486508
487509
:not(:test(list > member > string))
488510
489511
Successive ``:not`` functions can be used to filter shapes using several
490512
predicates. The following example does not match strings or shapes with the
491513
:ref:`sensitive-trait` trait:
492514

493-
::
515+
.. code-block:: none
494516
495517
:not(string):not([trait|sensitive])
496518
@@ -499,15 +521,15 @@ match all of the provided predicates. The following selector finds all
499521
string shapes that do not have both the ``length`` and ``pattern``
500522
traits:
501523

502-
::
524+
.. code-block:: none
503525
504526
string:not([trait|length], [trait|pattern])
505527
506528
The following example matches all structure members that target strings in
507529
which the member does not have the ``length`` trait and the shape targeted by
508530
the member does not have the ``length`` trait:
509531

510-
::
532+
.. code-block:: none
511533
512534
structure > member
513535
:test(> string:not([trait|length]))
@@ -516,14 +538,14 @@ the member does not have the ``length`` trait:
516538
The following selector finds all service shapes that do not have a
517539
protocol trait applied to it:
518540

519-
::
541+
.. code-block:: none
520542
521543
service:not(:test(-[trait]-> [trait|protocolDefinition]))
522544
523545
The following selector finds all traits that are not attached to any shape
524546
in the model:
525547

526-
::
548+
.. code-block:: none
527549
528550
:not(* -[trait]-> *)[trait|trait]
529551
@@ -539,13 +561,13 @@ the member is matched.
539561

540562
The following example matches all structure members:
541563

542-
::
564+
.. code-block:: none
543565
544566
member:of(structure)
545567
546568
The following example matches all structure and list members:
547569

548-
::
570+
.. code-block:: none
549571
550572
member:of(structure, list)
551573
@@ -590,8 +612,9 @@ Selectors are defined by the following ABNF_ grammar.
590612
:/ "number"
591613
:/ "simpleType"
592614
:/ "collection"
593-
neighbors :">" / `directed_neighbor`
615+
neighbors :">" / `directed_neighbor` / `recursive_neighbor`
594616
directed_neighbor :"-[" `relationship_type` *("," `relationship_type`) "]->"
617+
recursive_neighbor :"~>"
595618
relationship_type :"identifier"
596619
:/ "create"
597620
:/ "read"

0 commit comments

Comments
 (0)