1+ /*
2+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+ * SPDX-License-Identifier: Apache-2.0
4+ */
5+ package software .amazon .smithy .python .aws .codegen ;
6+
7+ import static software .amazon .smithy .python .codegen .SymbolProperties .OPERATION_METHOD ;
8+
9+ import java .util .List ;
10+ import software .amazon .smithy .model .traits .InputTrait ;
11+ import software .amazon .smithy .model .traits .OutputTrait ;
12+ import software .amazon .smithy .python .codegen .GenerationContext ;
13+ import software .amazon .smithy .python .codegen .integrations .PythonIntegration ;
14+ import software .amazon .smithy .python .codegen .sections .*;
15+ import software .amazon .smithy .python .codegen .writer .PythonWriter ;
16+ import software .amazon .smithy .utils .CodeInterceptor ;
17+ import software .amazon .smithy .utils .CodeSection ;
18+
19+ public class AwsRstDocFileGenerator implements PythonIntegration {
20+
21+ @ Override
22+ public List <? extends CodeInterceptor <? extends CodeSection , PythonWriter >> interceptors (
23+ GenerationContext context
24+ ) {
25+ return List .of (
26+ // We generate custom RST files for each member that we want to have
27+ // its own page. This gives us much more fine-grained control of
28+ // what gets generated than just using automodule or autoclass on
29+ // the client would alone.
30+ new OperationGenerationInterceptor (context ),
31+ new StructureGenerationInterceptor (context ),
32+ new ErrorGenerationInterceptor (context ),
33+ new UnionGenerationInterceptor (context ),
34+ new UnionMemberGenerationInterceptor (context ));
35+ }
36+
37+ /**
38+ * Utility method to generate a header for documentation files.
39+ *
40+ * @param title The title of the section.
41+ * @return A formatted header string.
42+ */
43+ private static String generateHeader (String title ) {
44+ return String .format ("%s%n%s%n%n" , title , "=" .repeat (title .length ()));
45+ }
46+
47+ private static final class OperationGenerationInterceptor
48+ implements CodeInterceptor .Appender <OperationSection , PythonWriter > {
49+
50+ private final GenerationContext context ;
51+
52+ public OperationGenerationInterceptor (GenerationContext context ) {
53+ this .context = context ;
54+ }
55+
56+ @ Override
57+ public Class <OperationSection > sectionType () {
58+ return OperationSection .class ;
59+ }
60+
61+ @ Override
62+ public void append (PythonWriter pythonWriter , OperationSection section ) {
63+ var operation = section .operation ();
64+ var operationSymbol = context .symbolProvider ().toSymbol (operation ).expectProperty (OPERATION_METHOD );
65+ var input = context .model ().expectShape (operation .getInputShape ());
66+ var inputSymbol = context .symbolProvider ().toSymbol (input );
67+ var output = context .model ().expectShape (operation .getOutputShape ());
68+ var outputSymbol = context .symbolProvider ().toSymbol (output );
69+
70+ String operationName = operationSymbol .getName ();
71+ String inputSymbolName = inputSymbol .toString ();
72+ String outputSymbolName = outputSymbol .toString ();
73+ String serviceName = context .symbolProvider ().toSymbol (section .service ()).getName ();
74+ String docsFileName = String .format ("docs/client/%s.rst" , operationName );
75+ String fullOperationReference = String .format ("%s.client.%s.%s" ,
76+ context .settings ().moduleName (),
77+ serviceName ,
78+ operationName );
79+
80+ context .writerDelegator ().useFileWriter (docsFileName , "" , fileWriter -> {
81+ fileWriter .write (generateHeader (operationName ));
82+ fileWriter .write (".. automethod:: " + fullOperationReference + "\n \n " );
83+ fileWriter .write (".. toctree::\n :hidden:\n :maxdepth: 2\n \n " );
84+ fileWriter .write ("=================\n Input:\n =================\n \n " );
85+ fileWriter .write (".. autoclass:: " + inputSymbolName + "\n :members:\n " );
86+ fileWriter .write ("=================\n Output:\n =================\n \n " );
87+ fileWriter .write (".. autoclass:: " + outputSymbolName + "\n :members:\n " );
88+ });
89+ }
90+ }
91+
92+ private static final class StructureGenerationInterceptor
93+ implements CodeInterceptor .Appender <StructureSection , PythonWriter > {
94+
95+ private final GenerationContext context ;
96+
97+ public StructureGenerationInterceptor (GenerationContext context ) {
98+ this .context = context ;
99+ }
100+
101+ @ Override
102+ public Class <StructureSection > sectionType () {
103+ return StructureSection .class ;
104+ }
105+
106+ @ Override
107+ public void append (PythonWriter pythonWriter , StructureSection section ) {
108+ var shape = section .structure ();
109+ var symbol = context .symbolProvider ().toSymbol (shape );
110+ String docsFileName = String .format ("docs/models/%s.rst" ,
111+ symbol .getName ());
112+ if (!shape .hasTrait (InputTrait .class ) && !shape .hasTrait (OutputTrait .class )) {
113+ context .writerDelegator ().useFileWriter (docsFileName , "" , writer -> {
114+ writer .write (generateHeader (symbol .getName ()));
115+ writer .write (".. autoclass:: " + symbol .toString () + "\n :members:\n " );
116+ });
117+ }
118+ }
119+ }
120+
121+ private static final class ErrorGenerationInterceptor
122+ implements CodeInterceptor .Appender <ErrorSection , PythonWriter > {
123+
124+ private final GenerationContext context ;
125+
126+ public ErrorGenerationInterceptor (GenerationContext context ) {
127+ this .context = context ;
128+ }
129+
130+ @ Override
131+ public Class <ErrorSection > sectionType () {
132+ return ErrorSection .class ;
133+ }
134+
135+ @ Override
136+ public void append (PythonWriter pythonWriter , ErrorSection section ) {
137+ var symbol = section .errorSymbol ();
138+ String docsFileName = String .format ("docs/models/%s.rst" ,
139+ symbol .getName ());
140+ context .writerDelegator ().useFileWriter (docsFileName , "" , writer -> {
141+ writer .write (generateHeader (symbol .getName ()));
142+ writer .write (".. autoexception:: " + symbol .toString () + "\n :members:\n :show-inheritance:\n " );
143+ });
144+ }
145+ }
146+
147+ private static final class UnionGenerationInterceptor
148+ implements CodeInterceptor .Appender <UnionSection , PythonWriter > {
149+
150+ private final GenerationContext context ;
151+
152+ public UnionGenerationInterceptor (GenerationContext context ) {
153+ this .context = context ;
154+ }
155+
156+ @ Override
157+ public Class <UnionSection > sectionType () {
158+ return UnionSection .class ;
159+ }
160+
161+ @ Override
162+ public void append (PythonWriter pythonWriter , UnionSection section ) {
163+ String parentName = section .parentName ();
164+ String docsFileName = String .format ("docs/models/%s.rst" , parentName );
165+ context .writerDelegator ().useFileWriter (docsFileName , "" , writer -> {
166+ writer .write (".. _" + parentName + ":\n \n " );
167+ writer .write (generateHeader (parentName ));
168+ writer .write (
169+ ".. autodata:: " + context .symbolProvider ().toSymbol (section .unionShape ()).toString () + " \n " );
170+ });
171+ }
172+ }
173+
174+ private static final class UnionMemberGenerationInterceptor
175+ implements CodeInterceptor .Appender <UnionMemberSection , PythonWriter > {
176+
177+ private final GenerationContext context ;
178+
179+ public UnionMemberGenerationInterceptor (GenerationContext context ) {
180+ this .context = context ;
181+ }
182+
183+ @ Override
184+ public Class <UnionMemberSection > sectionType () {
185+ return UnionMemberSection .class ;
186+ }
187+
188+ @ Override
189+ public void append (PythonWriter pythonWriter , UnionMemberSection section ) {
190+ var memberSymbol = section .memberSymbol ();
191+ String symbolName = memberSymbol .getName ();
192+ String docsFileName = String .format ("docs/models/%s.rst" , symbolName );
193+ context .writerDelegator ().useFileWriter (docsFileName , "" , writer -> {
194+ writer .write (".. _" + symbolName + ":\n \n " );
195+ writer .write (generateHeader (symbolName ));
196+ writer .write (".. autoclass:: " + memberSymbol .toString () + " \n " );
197+ });
198+ }
199+ }
200+ }
0 commit comments