Skip to content

Commit f76eee0

Browse files
JordonPhillipsmtdowling
authored andcommitted
Add input and output traits with inlined IO
This updates inlined IO shapes to automatically include the input and output traits.
1 parent 9a1a8e4 commit f76eee0

File tree

6 files changed

+105
-35
lines changed

6 files changed

+105
-35
lines changed

smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@
6161
import software.amazon.smithy.model.shapes.TimestampShape;
6262
import software.amazon.smithy.model.shapes.UnionShape;
6363
import software.amazon.smithy.model.traits.DocumentationTrait;
64+
import software.amazon.smithy.model.traits.InputTrait;
65+
import software.amazon.smithy.model.traits.OutputTrait;
6466
import software.amazon.smithy.model.traits.RequiredTrait;
6567
import software.amazon.smithy.model.traits.TraitFactory;
6668
import software.amazon.smithy.model.validation.Severity;
@@ -616,10 +618,12 @@ private void parseOperationStatement(ShapeId id, SourceLocation location) {
616618
parseProperties(id, propertyName -> {
617619
switch (propertyName) {
618620
case "input":
619-
parseInlineableOperationMember(id, operationInputSuffix, builder::input);
621+
TraitEntry inputTrait = new TraitEntry(InputTrait.ID.toString(), Node.objectNode(), true);
622+
parseInlineableOperationMember(id, operationInputSuffix, builder::input, inputTrait);
620623
break;
621624
case "output":
622-
parseInlineableOperationMember(id, operationOutputSuffix, builder::output);
625+
TraitEntry outputTrait = new TraitEntry(OutputTrait.ID.toString(), Node.objectNode(), true);
626+
parseInlineableOperationMember(id, operationOutputSuffix, builder::output, outputTrait);
623627
break;
624628
case "errors":
625629
parseIdList(builder::addError);
@@ -653,7 +657,12 @@ private void parseProperties(ShapeId id, Consumer<String> valueParser) {
653657
expect('}');
654658
}
655659

656-
private void parseInlineableOperationMember(ShapeId id, String suffix, Consumer<ShapeId> consumer) {
660+
private void parseInlineableOperationMember(
661+
ShapeId id,
662+
String suffix,
663+
Consumer<ShapeId> consumer,
664+
TraitEntry defaultTrait
665+
) {
657666
if (peek() == '=') {
658667
if (!modelFile.getVersion().supportsInlineOperationIO()) {
659668
throw syntax(id, "Inlined operation inputs and outputs can only be used with Smithy version 2 or "
@@ -662,15 +671,18 @@ private void parseInlineableOperationMember(ShapeId id, String suffix, Consumer<
662671
expect('=');
663672
clearPendingDocs();
664673
ws();
665-
consumer.accept(parseInlineStructure(id.getName() + suffix));
674+
consumer.accept(parseInlineStructure(id.getName() + suffix, defaultTrait));
666675
} else {
667676
ws();
668677
modelFile.addForwardReference(ParserUtils.parseShapeId(this), consumer);
669678
}
670679
}
671680

672-
private ShapeId parseInlineStructure(String name) {
681+
private ShapeId parseInlineStructure(String name, TraitEntry defaultTrait) {
673682
List<TraitEntry> traits = parseDocsAndTraits();
683+
if (defaultTrait != null) {
684+
traits.add(defaultTrait);
685+
}
674686
ShapeId id = ShapeId.fromRelative(modelFile.namespace(), name);
675687
SourceLocation location = currentLocation();
676688
parseMixins(id);

smithy-model/src/main/java/software/amazon/smithy/model/shapes/SmithyIdlModelSerializer.java

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.Collection;
2323
import java.util.Collections;
2424
import java.util.Comparator;
25+
import java.util.HashMap;
2526
import java.util.HashSet;
2627
import java.util.List;
2728
import java.util.Map;
@@ -42,9 +43,12 @@
4243
import software.amazon.smithy.model.traits.AnnotationTrait;
4344
import software.amazon.smithy.model.traits.DocumentationTrait;
4445
import software.amazon.smithy.model.traits.IdRefTrait;
46+
import software.amazon.smithy.model.traits.InputTrait;
47+
import software.amazon.smithy.model.traits.OutputTrait;
4548
import software.amazon.smithy.model.traits.RequiredTrait;
4649
import software.amazon.smithy.model.traits.Trait;
4750
import software.amazon.smithy.model.traits.TraitDefinition;
51+
import software.amazon.smithy.model.traits.UnitTypeTrait;
4852
import software.amazon.smithy.utils.CodeWriter;
4953
import software.amazon.smithy.utils.FunctionalUtils;
5054
import software.amazon.smithy.utils.ListUtils;
@@ -165,16 +169,20 @@ private Set<ShapeId> getInlineableShapes(Model fullModel, Collection<Shape> shap
165169
continue;
166170
}
167171
OperationShape operation = shape.asOperationShape().get();
168-
operation.getInput().ifPresent(shapeId -> {
169-
if (shapes.contains(fullModel.expectShape(shapeId))) {
170-
inlineableShapes.add(shapeId);
172+
if (!operation.getInputShape().equals(UnitTypeTrait.UNIT)) {
173+
Shape inputShape = fullModel.expectShape(operation.getInputShape());
174+
if (shapes.contains(inputShape) && inputShape.hasTrait(InputTrait.ID)
175+
&& operation.getInputShape().getName().equals(operation.getId().getName() + "Input")) {
176+
inlineableShapes.add(operation.getInputShape());
171177
}
172-
});
173-
operation.getOutput().ifPresent(shapeId -> {
174-
if (shapes.contains(fullModel.expectShape(shapeId))) {
175-
inlineableShapes.add(shapeId);
178+
}
179+
if (!operation.getOutputShape().equals(UnitTypeTrait.UNIT)) {
180+
Shape outputShape = fullModel.expectShape(operation.getOutputShape());
181+
if (shapes.contains(outputShape) && outputShape.hasTrait(OutputTrait.ID)
182+
&& operation.getOutputShape().getName().equals(operation.getId().getName() + "Output")) {
183+
inlineableShapes.add(operation.getOutputShape());
176184
}
177-
});
185+
}
178186
}
179187
return inlineableShapes;
180188
}
@@ -603,30 +611,33 @@ public Void operationShape(OperationShape shape) {
603611
serializeTraits(shape);
604612
codeWriter.openBlock("operation $L {", shape.getId().getName());
605613
List<MemberShape> mixinMembers = new ArrayList<>();
606-
mixinMembers.addAll(writeInlineableProperty(
607-
"input", shape.getInputShape(), shape.getId().getName() + "Input"));
608-
mixinMembers.addAll(writeInlineableProperty(
609-
"output", shape.getOutputShape(), shape.getId().getName() + "Output"));
614+
mixinMembers.addAll(writeInlineableProperty("input", shape.getInputShape(), InputTrait.ID));
615+
mixinMembers.addAll(writeInlineableProperty("output", shape.getOutputShape(), OutputTrait.ID));
610616
codeWriter.writeOptionalIdList("errors", shape.getErrors());
611617
codeWriter.closeBlock("}");
612618
codeWriter.write("");
613619
applyIntroducedTraits(mixinMembers);
614620
return null;
615621
}
616622

617-
private Collection<MemberShape> writeInlineableProperty(String key, ShapeId shapeId, String defaultName) {
623+
private Collection<MemberShape> writeInlineableProperty(String key, ShapeId shapeId, ShapeId defaultTrait) {
618624
if (!inlineableShapes.contains(shapeId)) {
619625
codeWriter.write("$L: $I", key, shapeId);
620626
return Collections.emptyList();
621627
}
622628

623629
StructureShape structure = model.expectShape(shapeId, StructureShape.class);
624-
if (structure.getAllTraits().isEmpty()) {
630+
if (hasOnlyDefaultTrait(structure, defaultTrait)) {
625631
codeWriter.writeInline("$L := ", key);
626632
} else {
627633
codeWriter.write("$L := ", key);
628634
codeWriter.indent();
629-
serializeTraits(structure);
635+
Map<ShapeId, Trait> traits = structure.getAllTraits();
636+
if (defaultTrait != null) {
637+
traits = new HashMap<>(traits);
638+
traits.remove(defaultTrait);
639+
}
640+
serializeTraits(traits);
630641
}
631642

632643
List<MemberShape> nonMixinMembers = new ArrayList<>();
@@ -642,12 +653,16 @@ private Collection<MemberShape> writeInlineableProperty(String key, ShapeId shap
642653
writeMixins(structure, !nonMixinMembers.isEmpty());
643654
writeShapeMembers(nonMixinMembers, true);
644655

645-
if (!structure.getAllTraits().isEmpty()) {
656+
if (!hasOnlyDefaultTrait(structure, defaultTrait)) {
646657
codeWriter.dedent();
647658
}
648659

649660
return mixinMembers;
650661
}
662+
663+
private boolean hasOnlyDefaultTrait(Shape shape, ShapeId defaultTrait) {
664+
return shape.getAllTraits().size() == 1 && shape.hasTrait(defaultTrait);
665+
}
651666
}
652667

653668
/**

smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/doc-comments.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,16 @@
5151
}
5252
},
5353
"smithy.example#MyOperationInput": {
54-
"type": "structure"
54+
"type": "structure",
55+
"traits": {
56+
"smithy.api#input": {}
57+
}
5558
},
5659
"smithy.example#MyOperationOutput": {
5760
"type": "structure",
5861
"traits": {
59-
"smithy.api#documentation": "These are not ignored because they come AFTER the walrus\noperator."
62+
"smithy.api#documentation": "These are not ignored because they come AFTER the walrus\noperator.",
63+
"smithy.api#output": {}
6064
}
6165
}
6266
}

smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/custom-suffix.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,16 @@
1111
}
1212
},
1313
"com.example#OperationRequest": {
14-
"type": "structure"
14+
"type": "structure",
15+
"traits": {
16+
"smithy.api#input": {}
17+
}
1518
},
1619
"com.example#OperationResponse": {
17-
"type": "structure"
20+
"type": "structure",
21+
"traits": {
22+
"smithy.api#output": {}
23+
}
1824
}
1925
}
2026
}

smithy-model/src/test/resources/software/amazon/smithy/model/loader/valid/inline-io/inline-io.json

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
"id": {
1717
"target": "smithy.api#String"
1818
}
19+
},
20+
"traits": {
21+
"smithy.api#input": {}
1922
}
2023
},
2124
"com.example#DerivedNamesOutput": {
@@ -24,6 +27,9 @@
2427
"id": {
2528
"target": "smithy.api#String"
2629
}
30+
},
31+
"traits": {
32+
"smithy.api#output": {}
2733
}
2834
},
2935
"com.example#UsesTraits": {
@@ -43,7 +49,8 @@
4349
}
4450
},
4551
"traits": {
46-
"smithy.api#sensitive": {}
52+
"smithy.api#sensitive": {},
53+
"smithy.api#input": {}
4754
}
4855
},
4956
"com.example#UsesTraitsOutput": {
@@ -54,7 +61,8 @@
5461
}
5562
},
5663
"traits": {
57-
"smithy.api#sensitive": {}
64+
"smithy.api#sensitive": {},
65+
"smithy.api#output": {}
5866
}
5967
},
6068
"com.example#NameBearer": {
@@ -88,7 +96,10 @@
8896
{
8997
"target": "com.example#NameBearer"
9098
}
91-
]
99+
],
100+
"traits": {
101+
"smithy.api#input": {}
102+
}
92103
},
93104
"com.example#UsesMixinsOutput": {
94105
"type": "structure",
@@ -101,7 +112,10 @@
101112
{
102113
"target": "com.example#NameBearer"
103114
}
104-
]
115+
],
116+
"traits": {
117+
"smithy.api#output": {}
118+
}
105119
},
106120
"com.example#UsesTraitsAndMixins": {
107121
"type": "operation",
@@ -120,7 +134,8 @@
120134
}
121135
},
122136
"traits": {
123-
"smithy.api#sensitive": {}
137+
"smithy.api#sensitive": {},
138+
"smithy.api#input": {}
124139
},
125140
"mixins": [
126141
{
@@ -136,7 +151,8 @@
136151
}
137152
},
138153
"traits": {
139-
"smithy.api#sensitive": {}
154+
"smithy.api#sensitive": {},
155+
"smithy.api#output": {}
140156
},
141157
"mixins": [
142158
{
@@ -154,10 +170,16 @@
154170
}
155171
},
156172
"com.example#EmptyShapesInput": {
157-
"type": "structure"
173+
"type": "structure",
174+
"traits": {
175+
"smithy.api#input": {}
176+
}
158177
},
159178
"com.example#EmptyShapesOutput": {
160-
"type": "structure"
179+
"type": "structure",
180+
"traits": {
181+
"smithy.api#output": {}
182+
}
161183
},
162184
"com.example#HasDocComments": {
163185
"type": "operation",
@@ -171,13 +193,15 @@
171193
"com.example#HasDocCommentsInput": {
172194
"type": "structure",
173195
"traits": {
174-
"smithy.api#documentation": "The trait parser automagically handles these"
196+
"smithy.api#documentation": "The trait parser automagically handles these",
197+
"smithy.api#input": {}
175198
}
176199
},
177200
"com.example#HasDocCommentsOutput": {
178201
"type": "structure",
179202
"traits": {
180-
"smithy.api#documentation": "Here too"
203+
"smithy.api#documentation": "Here too",
204+
"smithy.api#output": {}
181205
}
182206
}
183207
}

smithy-model/src/test/resources/software/amazon/smithy/model/shapes/idl-serialization/cases/inline-io.smithy

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ operation DerivedNames {
1111
}
1212
}
1313

14+
operation DoesNotInlineWithoutIOTrait {
15+
input: DoesNotInlineWithoutIOTraitInput
16+
output: DoesNotInlineWithoutIOTraitOutput
17+
}
18+
1419
operation EmptyShapes {
1520
input := {}
1621
output := {}
@@ -60,6 +65,10 @@ operation UsesTraitsAndMixins {
6065
}
6166
}
6267

68+
structure DoesNotInlineWithoutIOTraitInput {}
69+
70+
structure DoesNotInlineWithoutIOTraitOutput {}
71+
6372
@mixin
6473
structure NameBearer {
6574
name: String

0 commit comments

Comments
 (0)