66
77import java .util .HashSet ;
88import java .util .Set ;
9+ import java .util .logging .Logger ;
910import software .amazon .smithy .model .Model ;
1011import software .amazon .smithy .model .loader .Prelude ;
1112import software .amazon .smithy .model .selector .Selector ;
2526import software .amazon .smithy .model .transform .ModelTransformer ;
2627
2728final class CreateSyntheticService {
29+ private static final Logger LOGGER = Logger .getLogger (CreateSyntheticService .class .getName ());
2830 private static final String SYNTHETIC_NAMESPACE = "smithy.synthetic" ;
2931 private static final String SYNTHETIC_OPERATION_NAME = "TypesGenOperation" ;
3032 static final ShapeId SYNTHETIC_SERVICE_ID = ShapeId .fromParts (SYNTHETIC_NAMESPACE , "TypesGenService" );
@@ -33,6 +35,8 @@ final class CreateSyntheticService {
3335 ShapeType .UNION ,
3436 ShapeType .ENUM ,
3537 ShapeType .INT_ENUM );
38+ private static final Set <ShapeId > TRAIT_BLOCKLIST =
39+ Set .of (MixinTrait .ID , ProtocolDefinitionTrait .ID , TraitDefinition .ID , PrivateTrait .ID );
3640
3741 private final Selector selector ;
3842 private final boolean includeInputsAndOutputs ;
@@ -54,24 +58,36 @@ Model transform(ModelTransformer transformer, Model model) {
5458 }
5559
5660 var operationId = ShapeId .fromParts (SYNTHETIC_NAMESPACE , SYNTHETIC_OPERATION_NAME + index );
57- var inputId = ShapeId .fromParts (SYNTHETIC_NAMESPACE , operationId .getName () + "Input" );
58- var inputBuilder = StructureShape .builder ()
59- .id (inputId )
60- .addTrait (new InputTrait ());
61- var operationBuilder = OperationShape .builder ().id (operationId ).input (inputId );
62- serviceBuilder .addOperation (operationId );
61+ var operationBuilder = OperationShape .builder ().id (operationId );
6362
6463 if (shape .hasTrait (ErrorTrait .class )) {
6564 operationBuilder .addError (shape .getId ());
65+ } else if (shape .hasTrait (InputTrait .class )) {
66+ operationBuilder .input (shape );
67+ } else if (shape .hasTrait (OutputTrait .class )) {
68+ operationBuilder .output (shape );
6669 } else {
67- inputBuilder .addMember ("member" , shape .getId ());
70+ var input = StructureShape .builder ()
71+ .id (ShapeId .fromParts (SYNTHETIC_NAMESPACE , operationId .getName () + "Input" ))
72+ .addTrait (new InputTrait ())
73+ .addMember ("member" , shape .getId ())
74+ .build ();
6875 index ++;
76+ operationBuilder .input (input );
77+ addedShapes .add (input );
6978 }
70- addedShapes .add (inputBuilder .build ());
79+
80+ serviceBuilder .addOperation (operationId );
7181 addedShapes .add (operationBuilder .build ());
7282 }
7383
7484 addedShapes .add (serviceBuilder .build ());
85+
86+ // First, remove all existing operations and services. We don't need them, and they'll just get in the way
87+ // of adding input and output shapes if we're doing that.
88+ model = transformer .removeShapesIf (model , shape -> shape .isOperationShape () || shape .isServiceShape ());
89+
90+ // Then add our new service and operation so that we can generate types.
7591 model = transformer .replaceShapes (model , addedShapes );
7692
7793 // Ensure validation gets run so we aren't generating anything too crazy
@@ -80,16 +96,26 @@ Model transform(ModelTransformer transformer, Model model) {
8096 }
8197
8298 private boolean shouldGenerate (Shape shape ) {
83- if (!GENERATED_TYPES .contains (shape .getType ())
84- || shape .hasTrait (MixinTrait .class )
85- || shape .hasTrait (ProtocolDefinitionTrait .class )
86- || shape .hasTrait (TraitDefinition .class )
87- || shape .hasTrait (PrivateTrait .class )
88- || Prelude .isPreludeShape (shape )) {
99+ if (!GENERATED_TYPES .contains (shape .getType ()) || Prelude .isPreludeShape (shape )) {
89100 return false ;
90101 }
91102
92- return includeInputsAndOutputs
93- || (!shape .hasTrait (InputTrait .class ) && !shape .hasTrait (OutputTrait .class ));
103+ if (!includeInputsAndOutputs && (shape .hasTrait (InputTrait .class ) || shape .hasTrait (OutputTrait .class ))) {
104+ LOGGER .finest ("""
105+ Skipping generating Python type for shape `%s` because it is either an input or output shape. \
106+ To generate this shape anyway, set "generateInputsAndOutputs" to true.""" .formatted (shape .getId ()));
107+ return false ;
108+ }
109+
110+ for (var blockedTrait : TRAIT_BLOCKLIST ) {
111+ if (shape .hasTrait (blockedTrait )) {
112+ LOGGER .finest ("Skipping generating Python type for shape `%s` because it has trait `%s`" .formatted (
113+ shape .getId (),
114+ blockedTrait ));
115+ return false ;
116+ }
117+ }
118+
119+ return true ;
94120 }
95121}
0 commit comments