Skip to content

Commit

Permalink
Add block apply statements
Browse files Browse the repository at this point in the history
It is a common requirement to need to apply multiple traits to the same
shape. However, this previously required that the shape ID of the trait
is restated for each applied trait. This added a lot of noise in models,
especially when applying traits to members. With this change, multiple
traits can be applied to a shape using a single statement by wrapping
traits in a block ("{" and "}").
  • Loading branch information
mtdowling committed Aug 9, 2021
1 parent a891f51 commit e39be9d
Show file tree
Hide file tree
Showing 12 changed files with 143 additions and 9 deletions.
43 changes: 38 additions & 5 deletions docs/source/1.0/spec/core/idl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,9 @@ The Smithy IDL is defined by the following ABNF:
trait_body_value :`trait_structure` / `node_value`
trait_structure :`trait_structure_kvp` *(`ws` `trait_structure_kvp`)
trait_structure_kvp :`node_object_key` `ws` ":" `ws` `node_value`
apply_statement :"apply" `ws` `shape_id` `ws` `trait` `ws`
apply_statement :`apply_statement_singular` / `apply_statement_block`
apply_statement_singular: "apply" `ws` `shape_id` `ws` `trait` `ws`
apply_statement_block: "apply" `ws` `shape_id` `ws` "{" `trait_statements` "}"
.. rubric:: Shape ID

Expand Down Expand Up @@ -1491,22 +1493,52 @@ Apply statement
Traits can be applied to shapes outside of a shape's definition using an
:token:`apply_statement`.

The following example applies the :ref:`documentation-trait` and
:ref:`length-trait` to the ``smithy.example#MyString`` shape:
The following example applies the :ref:`documentation-trait` to the
``smithy.example#MyString`` shape:

.. tabs::

.. code-tab:: smithy

$version: "1.1"
namespace smithy.example

apply MyString @documentation("This is my string!")
apply MyString @length(min: 1, max: 10)

.. code-tab:: json

{
"smithy": "1.0",
"smithy": "1.1",
"shapes": {
"smithy.example#MyString": {
"type": "apply",
"traits": {
"smithy.api#documentation": "This is my string!"
}
}
}
}

Multiple traits can be applied to the same shape using a block apply
statement. The following example applies the :ref:`documentation-trait`
and :ref:`length-trait` to the ``smithy.example#MyString`` shape:

.. tabs::

.. code-tab:: smithy

$version: "1.1"
namespace smithy.example

apply MyString {
@documentation("This is my string!")
@length(min: 1, max: 10)
}

.. code-tab:: json

{
"smithy": "1.1",
"shapes": {
"smithy.example#MyString": {
"type": "apply",
Expand All @@ -1525,6 +1557,7 @@ Traits can be applied to members too:

.. code-block:: smithy
$version: "1.1"
namespace smithy.example
apply MyStructure$foo @documentation("Structure member documentation")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,12 +667,22 @@ private void parseApplyStatement() {
String name = ParserUtils.parseShapeId(this);
ws();

TraitEntry traitEntry = IdlTraitParser.parseTraitValue(this);
// Account for singular or block apply statements.
List<TraitEntry> traitsToApply;
if (peek() == '{') {
expect('{');
ws();
traitsToApply = IdlTraitParser.parseTraits(this);
expect('}');
} else {
traitsToApply = Collections.singletonList(IdlTraitParser.parseTraitValue(this));
}

// First, resolve the targeted shape.
modelFile.addForwardReference(name, id -> {
// Next, resolve the trait ID.
onDeferredTrait(id, traitEntry.traitName, traitEntry.value, traitEntry.isAnnotation);
for (TraitEntry traitEntry : traitsToApply) {
onDeferredTrait(id, traitEntry.traitName, traitEntry.value, traitEntry.isAnnotation);
}
});

// Clear out any errantly captured pending docs.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Parse error at line 7, column 1 near ``: Expected: '}'
$version: "1.1"
namespace com.foo

apply SomeShape {
@deprecated
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"smithy": "1.1",
"shapes": {
"smithy.example#Foo": {
"type": "structure",
"members": {
"baz": {
"target": "smithy.api#String"
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
$version: "1.1"
namespace smithy.example

structure Foo {
baz: String,
}

// Block apply statements may be empty.
apply Foo$baz {}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"smithy": "1.0",
"smithy": "1.1",
"shapes": {
"smithy.example#Foo": {
"type": "structure",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
$version: "1.1"
namespace smithy.example

structure Foo {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"smithy": "1.1",
"shapes": {
"smithy.example#Foo": {
"type": "structure",
"members": {
"baz": {
"target": "smithy.api#String",
"traits": {
"smithy.api#documentation": "Hi",
"smithy.api#sensitive": {},
"smithy.api#deprecated": {}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
$version: "1.1"
namespace smithy.example

structure Foo {
baz: String,
}

apply Foo$baz{@documentation("Hi") @sensitive





@deprecated}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"smithy": "1.1",
"shapes": {
"smithy.example#Foo": {
"type": "structure",
"members": {
"baz": {
"target": "smithy.api#String",
"traits": {
"smithy.api#documentation": "Hi",
"smithy.api#sensitive": {},
"smithy.api#deprecated": {}
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
$version: "1.1"
namespace smithy.example

structure Foo {
baz: String,
}

apply Foo$baz {
@documentation("Hi")
@sensitive
@deprecated
}

0 comments on commit e39be9d

Please sign in to comment.