6
6
package software.amazon.smithy.rust.codegen.server.python.smithy.generators
7
7
8
8
import software.amazon.smithy.model.shapes.OperationShape
9
+ import software.amazon.smithy.model.traits.DocumentationTrait
9
10
import software.amazon.smithy.rust.codegen.rustlang.RustWriter
10
11
import software.amazon.smithy.rust.codegen.rustlang.asType
12
+ import software.amazon.smithy.rust.codegen.rustlang.rust
11
13
import software.amazon.smithy.rust.codegen.rustlang.rustBlockTemplate
12
14
import software.amazon.smithy.rust.codegen.rustlang.rustTemplate
13
15
import software.amazon.smithy.rust.codegen.server.python.smithy.PythonServerCargoDependency
14
16
import software.amazon.smithy.rust.codegen.server.smithy.ServerCargoDependency
15
17
import software.amazon.smithy.rust.codegen.smithy.CoreCodegenContext
18
+ import software.amazon.smithy.rust.codegen.smithy.Errors
19
+ import software.amazon.smithy.rust.codegen.smithy.Inputs
20
+ import software.amazon.smithy.rust.codegen.smithy.Outputs
21
+ import software.amazon.smithy.rust.codegen.util.getTrait
22
+ import software.amazon.smithy.rust.codegen.util.inputShape
23
+ import software.amazon.smithy.rust.codegen.util.outputShape
16
24
import software.amazon.smithy.rust.codegen.util.toSnakeCase
17
25
18
26
/* *
@@ -56,8 +64,10 @@ class PythonApplicationGenerator(
56
64
coreCodegenContext : CoreCodegenContext ,
57
65
private val operations : List <OperationShape >,
58
66
) {
67
+ private val crateName = coreCodegenContext.settings.moduleName
59
68
private val symbolProvider = coreCodegenContext.symbolProvider
60
69
private val runtimeConfig = coreCodegenContext.runtimeConfig
70
+ private val model = coreCodegenContext.model
61
71
private val codegenScope =
62
72
arrayOf(
63
73
" SmithyPython" to PythonServerCargoDependency .SmithyHttpServerPython (runtimeConfig).asType(),
@@ -73,10 +83,9 @@ class PythonApplicationGenerator(
73
83
)
74
84
75
85
fun render (writer : RustWriter ) {
86
+ renderPyApplicationRustDocs(writer)
76
87
writer.rustTemplate(
77
88
"""
78
- /// Main Python application, used to register operations and context and start multiple
79
- /// workers on the same shared socket.
80
89
##[#{pyo3}::pyclass]
81
90
##[derive(Debug, Clone)]
82
91
pub struct App {
@@ -127,7 +136,7 @@ class PythonApplicationGenerator(
127
136
)
128
137
rustBlockTemplate(
129
138
"""
130
- /// Dynamically codegenerate the routes, allowing to build the Smithy [Router].
139
+ /// Dynamically codegenerate the routes, allowing to build the Smithy [#{SmithyServer}:: Router].
131
140
pub fn build_router(&mut self, py: #{pyo3}::Python) -> #{pyo3}::PyResult<()>
132
141
""" ,
133
142
* codegenScope
@@ -179,4 +188,69 @@ class PythonApplicationGenerator(
179
188
}
180
189
}
181
190
}
191
+
192
+ private fun renderPyApplicationRustDocs (writer : RustWriter ) {
193
+ writer.rust(
194
+ """
195
+ ##[allow(clippy::tabs_in_doc_comments)]
196
+ /// Main Python application, used to register operations and context and start multiple
197
+ /// workers on the same shared socket.
198
+ ///
199
+ /// Operations can be registrered using the application object as a decorator (`@app.operation_name`).
200
+ ///
201
+ /// Here's a full example to get you started:
202
+ ///
203
+ /// ```python
204
+ ${ if (operations.any { it.errors.isNotEmpty() }) {
205
+ """ /// from $crateName import ${Inputs .namespace}
206
+ /// from $crateName import ${Outputs .namespace}
207
+ /// from $crateName import ${Errors .namespace} """
208
+ } else {
209
+ """ /// from $crateName import ${Inputs .namespace}
210
+ /// from $crateName import ${Outputs .namespace} """
211
+ } }
212
+ /// from $crateName import App
213
+ ///
214
+ /// @dataclass
215
+ /// class Context:
216
+ /// counter: int = 0
217
+ ///
218
+ /// app = App()
219
+ /// app.context(Context())
220
+ ///
221
+ ${operationImplementationStubs(operations)}
222
+ ///
223
+ /// app.run()
224
+ /// ```
225
+ ///
226
+ /// Any of operations above can be written as well prepending the `async` keyword and
227
+ /// the Python application will automatically handle it and schedule it on the event loop for you.
228
+ """
229
+ )
230
+ }
231
+
232
+ private fun operationImplementationStubs (operations : List <OperationShape >): String =
233
+ operations.joinToString(" \n ///\n " ) {
234
+ val operationDocumentation = it.getTrait<DocumentationTrait >()?.value
235
+ val ret = if (! operationDocumentation.isNullOrBlank()) {
236
+ operationDocumentation.replace(" #" , " ##" ).prependIndent(" /// ## " ) + " \n "
237
+ } else " "
238
+ ret +
239
+ """
240
+ /// ${it.signature()} :
241
+ /// raise NotImplementedError
242
+ """ .trimIndent()
243
+ }
244
+
245
+ /* *
246
+ * Returns the function signature for an operation handler implementation. Used in the documentation.
247
+ */
248
+ private fun OperationShape.signature (): String {
249
+ val inputSymbol = symbolProvider.toSymbol(inputShape(model))
250
+ val outputSymbol = symbolProvider.toSymbol(outputShape(model))
251
+ val inputT = " ${Inputs .namespace} ::${inputSymbol.name} "
252
+ val outputT = " ${Outputs .namespace} ::${outputSymbol.name} "
253
+ val operationName = symbolProvider.toSymbol(this ).name.toSnakeCase()
254
+ return " @app.$operationName \n /// def $operationName (input: $inputT , ctx: Context) -> $outputT "
255
+ }
182
256
}
0 commit comments