mirror of https://github.com/smithy-lang/smithy-rs
Generate documentation for structures (#47)
* Generate documentation for structures The first of many commits to generate docs. This is a first pass for structures, we still need to document enums & unions. Some serious design also needs to occur to figure out the best practice for turning the Smithy documentation into nice Rust documentation. * Bump Java version to 9 * Remove exclusion of `target` * Don't build docs for deps * Update to use getMemberTrait
This commit is contained in:
parent
8884657c3a
commit
be16c2f225
|
@ -4,6 +4,7 @@ name: CI
|
|||
|
||||
env:
|
||||
rust_version: 1.48.0
|
||||
java_version: 9
|
||||
|
||||
jobs:
|
||||
style:
|
||||
|
@ -14,7 +15,7 @@ jobs:
|
|||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: ${{ env.java_version }}
|
||||
- uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
|
@ -51,7 +52,7 @@ jobs:
|
|||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: ${{ env.java_version }}
|
||||
- name: test
|
||||
run: ./gradlew :codegen:test
|
||||
integration-tests:
|
||||
|
@ -82,7 +83,7 @@ jobs:
|
|||
- name: Set up JDK
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 8
|
||||
java-version: ${{ env.java_version }}
|
||||
- name: integration-tests
|
||||
run: ./gradlew :codegen-test:test
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
@ -94,7 +95,7 @@ jobs:
|
|||
path: |
|
||||
codegen-test/build/smithyprojections/codegen-test/*/rust-codegen/
|
||||
codegen-test/build/smithyprojections/codegen-test/Cargo.toml
|
||||
!**/target
|
||||
codegen-test/build/smithyprojections/codegen-test/target/doc
|
||||
runtime-tests:
|
||||
name: Rust runtime tests
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
@ -110,6 +110,14 @@ tasks.register<Exec>("cargoTest") {
|
|||
dependsOn("build")
|
||||
}
|
||||
|
||||
tasks.register<Exec>("cargoDocs") {
|
||||
workingDir("build/smithyprojections/codegen-test/")
|
||||
// disallow warnings
|
||||
environment("RUSTFLAGS", "-D warnings")
|
||||
commandLine("cargo", "doc", "--no-deps")
|
||||
dependsOn("build")
|
||||
}
|
||||
|
||||
tasks.register<Exec>("cargoClippy") {
|
||||
workingDir("build/smithyprojections/codegen-test/")
|
||||
// disallow warnings
|
||||
|
@ -118,7 +126,7 @@ tasks.register<Exec>("cargoClippy") {
|
|||
dependsOn("build")
|
||||
}
|
||||
|
||||
tasks["test"].finalizedBy("cargoCheck", "cargoClippy", "cargoTest")
|
||||
tasks["test"].finalizedBy("cargoCheck", "cargoClippy", "cargoTest", "cargoDocs")
|
||||
|
||||
tasks["clean"].doFirst {
|
||||
delete("smithy-build.json")
|
||||
|
|
|
@ -5,4 +5,16 @@ data class RustModule(val name: String, val rustMetadata: RustMetadata) {
|
|||
rustMetadata.render(writer)
|
||||
writer.write("mod $name;")
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun default(name: String, public: Boolean): RustModule {
|
||||
// TODO: figure out how to enable this, but only for real services (protocol tests don't have documentation)
|
||||
/*val attributes = if (public) {
|
||||
listOf(Custom("deny(missing_docs)"))
|
||||
} else {
|
||||
listOf()
|
||||
}*/
|
||||
return RustModule(name, RustMetadata(public = public))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,16 @@ import software.amazon.smithy.codegen.core.CodegenException
|
|||
import software.amazon.smithy.codegen.core.Symbol
|
||||
import software.amazon.smithy.codegen.core.writer.CodegenWriter
|
||||
import software.amazon.smithy.codegen.core.writer.CodegenWriterFactory
|
||||
import software.amazon.smithy.model.Model
|
||||
import software.amazon.smithy.model.shapes.CollectionShape
|
||||
import software.amazon.smithy.model.shapes.Shape
|
||||
import software.amazon.smithy.model.shapes.ShapeId
|
||||
import software.amazon.smithy.model.traits.DocumentationTrait
|
||||
import software.amazon.smithy.model.traits.EnumTrait
|
||||
import software.amazon.smithy.rust.codegen.smithy.RuntimeType
|
||||
import software.amazon.smithy.rust.codegen.smithy.isOptional
|
||||
import software.amazon.smithy.rust.codegen.smithy.rustType
|
||||
import software.amazon.smithy.rust.codegen.util.orNull
|
||||
import software.amazon.smithy.utils.CodeWriter
|
||||
import java.util.function.BiFunction
|
||||
|
||||
|
@ -74,11 +77,55 @@ fun <T : CodeWriter> T.rustBlock(header: String, vararg args: Any, block: T.() -
|
|||
return this
|
||||
}
|
||||
|
||||
class RustWriter private constructor(private val filename: String, val namespace: String, private val commentCharacter: String = "//", private val printWarning: Boolean = true) :
|
||||
/**
|
||||
* Generate a RustDoc comment for [shape]
|
||||
*/
|
||||
fun <T : CodeWriter> T.documentShape(shape: Shape, model: Model): T {
|
||||
// TODO: support additional Smithy documentation traits like @example
|
||||
val docTrait = shape.getMemberTrait(model, DocumentationTrait::class.java).orNull()
|
||||
|
||||
docTrait?.value?.also {
|
||||
this.docs(it)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Write RustDoc-style docs into the writer
|
||||
*
|
||||
* Several modifications are made to provide consistent RustDoc formatting:
|
||||
* - All lines will be prefixed by `///`
|
||||
* - Tabs are replaced with spaces
|
||||
* - Empty newlines are removed
|
||||
*/
|
||||
fun <T : CodeWriter> T.docs(text: String, vararg args: Any) {
|
||||
pushState("docs")
|
||||
setNewlinePrefix("/// ")
|
||||
val cleaned = text.lines()
|
||||
// We need to filter out blank lines—an empty line causes the markdown parser to interpret the subsequent
|
||||
// docs as a code block because they are indented.
|
||||
.filter { !it.isBlank() }
|
||||
.joinToString("\n") {
|
||||
// Rustdoc warns on tabs in documentation
|
||||
it.trimStart().replace("\t", " ")
|
||||
}
|
||||
write(cleaned, *args)
|
||||
popState()
|
||||
}
|
||||
|
||||
class RustWriter private constructor(
|
||||
private val filename: String,
|
||||
val namespace: String,
|
||||
private val commentCharacter: String = "//",
|
||||
private val printWarning: Boolean = true
|
||||
) :
|
||||
CodegenWriter<RustWriter, UseDeclarations>(null, UseDeclarations(namespace)) {
|
||||
companion object {
|
||||
fun forModule(module: String): RustWriter {
|
||||
return RustWriter("$module.rs", "crate::$module")
|
||||
fun forModule(module: String?): RustWriter = if (module == null) {
|
||||
RustWriter("lib.rs", "crate")
|
||||
} else {
|
||||
RustWriter("$module.rs", "crate::$module")
|
||||
}
|
||||
|
||||
val Factory: CodegenWriterFactory<RustWriter> =
|
||||
|
@ -89,17 +136,16 @@ class RustWriter private constructor(private val filename: String, val namespace
|
|||
}
|
||||
}
|
||||
}
|
||||
init {
|
||||
if (filename.endsWith(".rs")) {
|
||||
require(namespace.startsWith("crate")) { "We can only write into files in the crate (got $namespace)" }
|
||||
}
|
||||
}
|
||||
|
||||
private val formatter = RustSymbolFormatter()
|
||||
private var n = 0
|
||||
|
||||
init {
|
||||
if (filename.endsWith(".rs")) {
|
||||
require(namespace.startsWith("crate")) { "We can only write into files in the crate (got $namespace)" }
|
||||
}
|
||||
putFormatter('T', formatter)
|
||||
putFormatter('D', RustDocLinker())
|
||||
}
|
||||
|
||||
fun module(): String? = if (filename.endsWith(".rs")) {
|
||||
|
@ -125,7 +171,11 @@ class RustWriter private constructor(private val filename: String, val namespace
|
|||
*
|
||||
* The returned writer will inject any local imports into the module as needed.
|
||||
*/
|
||||
fun withModule(moduleName: String, rustMetadata: RustMetadata = RustMetadata(public = true), moduleWriter: RustWriter.() -> Unit) {
|
||||
fun withModule(
|
||||
moduleName: String,
|
||||
rustMetadata: RustMetadata = RustMetadata(public = true),
|
||||
moduleWriter: RustWriter.() -> Unit
|
||||
): RustWriter {
|
||||
// In Rust, modules must specify their own imports—they don't have access to the parent scope.
|
||||
// To easily handle this, create a new inner writer to collect imports, then dump it
|
||||
// into an inline module.
|
||||
|
@ -136,6 +186,7 @@ class RustWriter private constructor(private val filename: String, val namespace
|
|||
write(innerWriter.toString())
|
||||
}
|
||||
innerWriter.dependencies.forEach { addDependency(it) }
|
||||
return this
|
||||
}
|
||||
|
||||
// TODO: refactor both of these methods & add a parent method to for_each across any field type
|
||||
|
@ -186,6 +237,18 @@ class RustWriter private constructor(private val filename: String, val namespace
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate RustDoc links, eg. [`Abc`](crate::module::Abc)
|
||||
*/
|
||||
inner class RustDocLinker : BiFunction<Any, String, String> {
|
||||
override fun apply(t: Any, u: String): String {
|
||||
return when (t) {
|
||||
is Symbol -> "[`${t.name}`](${t.fullName})"
|
||||
else -> throw CodegenException("Invalid type provided to RustDocLinker ($t) expected Symbol")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class RustSymbolFormatter : BiFunction<Any, String, String> {
|
||||
override fun apply(t: Any, u: String): String {
|
||||
return when (t) {
|
||||
|
|
|
@ -20,7 +20,6 @@ import software.amazon.smithy.model.traits.EnumTrait
|
|||
import software.amazon.smithy.rust.codegen.lang.CargoDependency
|
||||
import software.amazon.smithy.rust.codegen.lang.InlineDependency
|
||||
import software.amazon.smithy.rust.codegen.lang.RustDependency
|
||||
import software.amazon.smithy.rust.codegen.lang.RustMetadata
|
||||
import software.amazon.smithy.rust.codegen.lang.RustModule
|
||||
import software.amazon.smithy.rust.codegen.lang.RustWriter
|
||||
import software.amazon.smithy.rust.codegen.smithy.generators.CargoTomlGenerator
|
||||
|
@ -103,8 +102,8 @@ class CodegenVisitor(context: PluginContext) : ShapeVisitor.Default<Unit>() {
|
|||
}
|
||||
writers.useFileWriter("src/lib.rs", "crate::lib") { writer ->
|
||||
val includedModules = writers.includedModules().toSet().filter { it != "lib" }
|
||||
val modules = includedModules.map {
|
||||
RustModule(it, RustMetadata(public = PublicModules.contains(it)))
|
||||
val modules = includedModules.map { moduleName ->
|
||||
RustModule.default(moduleName, PublicModules.contains(moduleName))
|
||||
}
|
||||
LibRsGenerator(modules).render(writer)
|
||||
}
|
||||
|
|
|
@ -13,8 +13,11 @@ import software.amazon.smithy.model.traits.ErrorTrait
|
|||
import software.amazon.smithy.rust.codegen.lang.RustType
|
||||
import software.amazon.smithy.rust.codegen.lang.RustWriter
|
||||
import software.amazon.smithy.rust.codegen.lang.conditionalBlock
|
||||
import software.amazon.smithy.rust.codegen.lang.docs
|
||||
import software.amazon.smithy.rust.codegen.lang.documentShape
|
||||
import software.amazon.smithy.rust.codegen.lang.render
|
||||
import software.amazon.smithy.rust.codegen.lang.rustBlock
|
||||
import software.amazon.smithy.rust.codegen.lang.stripOuter
|
||||
import software.amazon.smithy.rust.codegen.smithy.RuntimeType
|
||||
import software.amazon.smithy.rust.codegen.smithy.canUseDefault
|
||||
import software.amazon.smithy.rust.codegen.smithy.expectRustMetadata
|
||||
|
@ -24,8 +27,6 @@ import software.amazon.smithy.rust.codegen.smithy.rustType
|
|||
import software.amazon.smithy.rust.codegen.util.dq
|
||||
import software.amazon.smithy.utils.CaseUtils
|
||||
|
||||
// TODO(maybe): extract struct generation from Smithy shapes to support generating body objects
|
||||
// TODO: generate documentation
|
||||
class StructureGenerator(
|
||||
val model: Model,
|
||||
private val symbolProvider: SymbolProvider,
|
||||
|
@ -47,6 +48,8 @@ class StructureGenerator(
|
|||
}
|
||||
if (renderBuilder) {
|
||||
val symbol = symbolProvider.toSymbol(shape)
|
||||
// TODO: figure out exactly what docs we want on a the builder module
|
||||
writer.docs("See \$D", symbol)
|
||||
writer.withModule(symbol.name.toSnakeCase()) {
|
||||
renderBuilder(this)
|
||||
}
|
||||
|
@ -83,13 +86,14 @@ class StructureGenerator(
|
|||
|
||||
private fun renderStructure() {
|
||||
val symbol = symbolProvider.toSymbol(shape)
|
||||
// TODO(maybe): Pull derive info from the symbol so that the symbol provider can alter things as necessary; 4h
|
||||
val containerMeta = symbol.expectRustMetadata()
|
||||
writer.documentShape(shape, model)
|
||||
containerMeta.render(writer)
|
||||
|
||||
writer.rustBlock("struct ${symbol.name} ${lifetimeDeclaration()}") {
|
||||
members.forEach { member ->
|
||||
val memberName = symbolProvider.toMemberName(member)
|
||||
writer.documentShape(member, model)
|
||||
symbolProvider.toSymbol(member).expectRustMetadata().render(this)
|
||||
write("$memberName: \$T,", symbolProvider.toSymbol(member))
|
||||
}
|
||||
|
@ -97,6 +101,7 @@ class StructureGenerator(
|
|||
|
||||
if (renderBuilder) {
|
||||
writer.rustBlock("impl ${symbol.name}") {
|
||||
docs("Creates a new builder-style object to manufacture \$D", symbol)
|
||||
rustBlock("pub fn builder() -> \$T", builderSymbol) {
|
||||
write("\$T::default()", builderSymbol)
|
||||
}
|
||||
|
@ -105,11 +110,10 @@ class StructureGenerator(
|
|||
}
|
||||
|
||||
private fun renderBuilder(writer: RustWriter) {
|
||||
// Eventually, I want to do a fancier module layout:
|
||||
// model/some_model.rs [contains builder and impl for a single model] struct SomeModel, struct Builder
|
||||
// model/mod.rs [contains pub use for each model to bring it into top level scope]
|
||||
// users will do models::SomeModel, models::SomeModel::builder()
|
||||
val builderName = "Builder"
|
||||
|
||||
val symbol = symbolProvider.toSymbol(shape)
|
||||
writer.docs("A builder for \$D", symbol)
|
||||
writer.write("#[non_exhaustive]")
|
||||
writer.write("#[derive(Debug, Clone, Default)]")
|
||||
writer.rustBlock("pub struct $builderName") {
|
||||
|
@ -134,17 +138,13 @@ class StructureGenerator(
|
|||
// All fields in the builder are optional
|
||||
val memberSymbol = symbolProvider.toSymbol(member)
|
||||
val outerType = memberSymbol.rustType()
|
||||
val coreType = outerType.let {
|
||||
when (it) {
|
||||
is RustType.Option -> it.value
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
val coreType = outerType.stripOuter<RustType.Option>()
|
||||
val signature = when (coreType) {
|
||||
is RustType.String -> "<Str: Into<String>>(mut self, inp: Str) -> Self"
|
||||
is RustType.Box -> "<T>(mut self, inp: T) -> Self where T: Into<${coreType.render()}>"
|
||||
else -> "(mut self, inp: ${coreType.render()}) -> Self"
|
||||
}
|
||||
writer.documentShape(member, model)
|
||||
writer.rustBlock("pub fn $memberName$signature") {
|
||||
write("self.$memberName = Some(${builderConverter(coreType)});")
|
||||
write("self")
|
||||
|
@ -157,6 +157,7 @@ class StructureGenerator(
|
|||
false -> "\$T"
|
||||
}
|
||||
|
||||
writer.docs("Consumes the builder and constructs a \$D", symbol)
|
||||
rustBlock("pub fn build(self) -> $returnType", structureSymbol) {
|
||||
conditionalBlock("Ok(", ")", conditional = fallibleBuilder) {
|
||||
rustBlock("\$T", structureSymbol) {
|
||||
|
|
|
@ -33,7 +33,7 @@ import software.amazon.smithy.rust.codegen.smithy.Shapes
|
|||
import software.amazon.smithy.rust.codegen.smithy.isOptional
|
||||
import software.amazon.smithy.rust.codegen.smithy.referenceClosure
|
||||
import software.amazon.smithy.rust.codegen.smithy.rustType
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
class SymbolBuilderTest {
|
||||
|
@ -91,7 +91,7 @@ class SymbolBuilderTest {
|
|||
}
|
||||
])
|
||||
string StandardUnit
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val shape = model.expectShape(ShapeId.from("test#StandardUnit"))
|
||||
val provider: SymbolProvider = testSymbolProvider(model)
|
||||
val sym = provider.toSymbol(shape)
|
||||
|
@ -257,7 +257,7 @@ class SymbolBuilderTest {
|
|||
// Sent in the body
|
||||
additional: String,
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val symbol = testSymbolProvider(model).toSymbol(model.expectShape(ShapeId.from("smithy.example#PutObject")))
|
||||
symbol.definitionFile shouldBe("src/${Operations.filename}")
|
||||
symbol.name shouldBe "PutObject"
|
||||
|
|
|
@ -16,7 +16,7 @@ import software.amazon.smithy.model.traits.EnumTrait
|
|||
import software.amazon.smithy.rust.codegen.lang.RustWriter
|
||||
import software.amazon.smithy.rust.codegen.smithy.generators.EnumGenerator
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndRun
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.shouldCompile
|
||||
|
@ -81,7 +81,7 @@ class EnumGeneratorTest {
|
|||
name: "Bar"
|
||||
}])
|
||||
string FooEnum
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val shape: StringShape = model.lookup("test#FooEnum")
|
||||
val trait = shape.expectTrait(EnumTrait::class.java)
|
||||
val writer = RustWriter.forModule("model")
|
||||
|
@ -109,7 +109,7 @@ class EnumGeneratorTest {
|
|||
value: "Bar",
|
||||
}])
|
||||
string FooEnum
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val shape: StringShape = model.lookup("test#FooEnum")
|
||||
val trait = shape.expectTrait(EnumTrait::class.java)
|
||||
val writer = RustWriter.forModule("model")
|
||||
|
@ -147,7 +147,7 @@ class EnumGeneratorTest {
|
|||
},
|
||||
])
|
||||
string FooEnum
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val shape = model.expectShape(ShapeId.from("test#FooEnum"), StringShape::class.java)
|
||||
val trait = shape.expectTrait(EnumTrait::class.java)
|
||||
val provider: SymbolProvider = testSymbolProvider(model)
|
||||
|
|
|
@ -30,7 +30,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.uriFormatString
|
|||
import software.amazon.smithy.rust.codegen.smithy.transformers.OperationNormalizer
|
||||
import software.amazon.smithy.rust.codegen.util.dq
|
||||
import software.amazon.smithy.rust.testutil.TestRuntimeConfig
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
|
@ -89,7 +89,7 @@ class HttpTraitBindingGeneratorTest {
|
|||
// Sent in the body
|
||||
additional: String,
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
private val model = OperationNormalizer().transformModel(baseModel)
|
||||
|
||||
private val operationShape = model.expectShape(ShapeId.from("smithy.example#PutObject"), OperationShape::class.java)
|
||||
|
|
|
@ -12,11 +12,15 @@ import software.amazon.smithy.model.shapes.MemberShape
|
|||
import software.amazon.smithy.model.shapes.Shape
|
||||
import software.amazon.smithy.model.shapes.ShapeId
|
||||
import software.amazon.smithy.model.shapes.StructureShape
|
||||
import software.amazon.smithy.rust.codegen.lang.Custom
|
||||
import software.amazon.smithy.rust.codegen.lang.RustMetadata
|
||||
import software.amazon.smithy.rust.codegen.lang.RustWriter
|
||||
import software.amazon.smithy.rust.codegen.lang.docs
|
||||
import software.amazon.smithy.rust.codegen.lang.rustBlock
|
||||
import software.amazon.smithy.rust.codegen.smithy.canUseDefault
|
||||
import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
|
@ -42,7 +46,7 @@ class StructureGeneratorTest {
|
|||
structure MyError {
|
||||
message: String
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
private val struct = model.expectShape(ShapeId.from("com.test#MyStruct"), StructureShape::class.java)
|
||||
private val inner = model.expectShape(ShapeId.from("com.test#Inner"), StructureShape::class.java)
|
||||
private val error = model.expectShape(ShapeId.from("com.test#MyError"), StructureShape::class.java)
|
||||
|
@ -141,4 +145,37 @@ class StructureGeneratorTest {
|
|||
generator.render()
|
||||
writer.compileAndTest()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `attach docs to everything`() {
|
||||
val model = """
|
||||
namespace com.test
|
||||
@documentation("inner doc")
|
||||
structure Inner { }
|
||||
|
||||
@documentation("shape doc")
|
||||
structure MyStruct {
|
||||
@documentation("member doc")
|
||||
member: String,
|
||||
|
||||
@documentation("specific docs")
|
||||
nested: Inner,
|
||||
|
||||
nested2: Inner
|
||||
}""".asSmithyModel()
|
||||
val provider: SymbolProvider = testSymbolProvider(model)
|
||||
val writer = RustWriter.forModule(null)
|
||||
writer.docs("module docs")
|
||||
writer
|
||||
.withModule(
|
||||
"model",
|
||||
// By attaching this lint, any missing documentation becomes a compiler erorr
|
||||
RustMetadata(additionalAttributes = listOf(Custom("deny(missing_docs)")), public = true)
|
||||
) {
|
||||
StructureGenerator(model, provider, this, model.lookup("com.test#Inner")).render()
|
||||
StructureGenerator(model, provider, this, model.lookup("com.test#MyStruct")).render()
|
||||
}
|
||||
|
||||
writer.compileAndTest()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ import software.amazon.smithy.rust.codegen.util.CommandFailed
|
|||
import software.amazon.smithy.rust.codegen.util.dq
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.TestRuntimeConfig
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
|
@ -69,7 +69,7 @@ class HttpProtocolTestGeneratorTest {
|
|||
|
||||
name: String
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
private val model = OperationNormalizer().transformModel(baseModel)
|
||||
private val symbolProvider = testSymbolProvider(model)
|
||||
private val runtimeConfig = TestRuntimeConfig
|
||||
|
|
|
@ -15,7 +15,7 @@ import software.amazon.smithy.rust.codegen.smithy.transformers.RecursiveShapeBox
|
|||
import software.amazon.smithy.rust.codegen.util.dq
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.TestRuntimeConfig
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
|
@ -59,7 +59,7 @@ class InstantiatorTest {
|
|||
member: WithBox,
|
||||
value: Integer
|
||||
}
|
||||
""".asSmithy().let { RecursiveShapeBoxer.transform(it) }
|
||||
""".asSmithyModel().let { RecursiveShapeBoxer.transform(it) }
|
||||
|
||||
private val symbolProvider = testSymbolProvider(model)
|
||||
private val runtimeConfig = TestRuntimeConfig
|
||||
|
|
|
@ -13,7 +13,7 @@ import software.amazon.smithy.model.shapes.StructureShape
|
|||
import software.amazon.smithy.rust.codegen.smithy.traits.InputBodyTrait
|
||||
import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
internal class OperationNormalizerTest {
|
||||
|
@ -23,7 +23,7 @@ internal class OperationNormalizerTest {
|
|||
val model = """
|
||||
namespace smithy.test
|
||||
operation Empty {}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val operationId = ShapeId.from("smithy.test#Empty")
|
||||
model.expectShape(operationId, OperationShape::class.java).input.isPresent shouldBe false
|
||||
val sut = OperationNormalizer()
|
||||
|
@ -48,7 +48,7 @@ internal class OperationNormalizerTest {
|
|||
operation MyOp {
|
||||
input: RenameMe
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val operationId = ShapeId.from("smithy.test#MyOp")
|
||||
model.expectShape(operationId, OperationShape::class.java).input.isPresent shouldBe true
|
||||
val sut = OperationNormalizer()
|
||||
|
@ -72,7 +72,7 @@ internal class OperationNormalizerTest {
|
|||
}
|
||||
operation MyOp {
|
||||
input: RenameMe
|
||||
}""".asSmithy()
|
||||
}""".asSmithyModel()
|
||||
|
||||
val sut = OperationNormalizer()
|
||||
val modified = sut.transformModel(model) { input ->
|
||||
|
|
|
@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test
|
|||
import software.amazon.smithy.model.shapes.MemberShape
|
||||
import software.amazon.smithy.rust.codegen.smithy.RustBoxTrait
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import kotlin.streams.toList
|
||||
|
||||
internal class RecursiveShapeBoxerTest {
|
||||
|
@ -23,7 +23,7 @@ internal class RecursiveShapeBoxerTest {
|
|||
structure Bar {
|
||||
hello: Hello
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
RecursiveShapeBoxer.transform(model) shouldBe model
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ internal class RecursiveShapeBoxerTest {
|
|||
RecursiveStruct: Recursive,
|
||||
anotherField: Boolean
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val transformed = RecursiveShapeBoxer.transform(model)
|
||||
val member: MemberShape = transformed.lookup("com.example#Recursive\$RecursiveStruct")
|
||||
member.expectTrait(RustBoxTrait::class.java)
|
||||
|
@ -62,7 +62,7 @@ internal class RecursiveShapeBoxerTest {
|
|||
otherMember: Atom,
|
||||
third: SecondTree
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val transformed = RecursiveShapeBoxer.transform(model)
|
||||
val boxed = transformed.shapes().filter { it.hasTrait(RustBoxTrait::class.java) }.toList()
|
||||
boxed.map { it.id.toString().removePrefix("com.example#") }.toSet() shouldBe setOf(
|
||||
|
|
|
@ -10,7 +10,7 @@ import software.amazon.smithy.rust.codegen.smithy.generators.StructureGenerator
|
|||
import software.amazon.smithy.rust.codegen.smithy.generators.UnionGenerator
|
||||
import software.amazon.smithy.rust.codegen.util.CommandFailed
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.asSmithy
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.testSymbolProvider
|
||||
|
||||
|
@ -36,7 +36,7 @@ class RecursiveShapesIntegrationTest {
|
|||
otherMember: Atom,
|
||||
third: SecondTree
|
||||
}
|
||||
""".asSmithy()
|
||||
""".asSmithyModel()
|
||||
val check = { input: Model ->
|
||||
val structures = listOf("Expr", "SecondTree").map { input.lookup<StructureShape>("com.example#$it") }
|
||||
val writer = RustWriter.forModule("model")
|
||||
|
|
|
@ -13,12 +13,17 @@ import software.amazon.smithy.codegen.core.SymbolProvider
|
|||
import software.amazon.smithy.model.Model
|
||||
import software.amazon.smithy.model.shapes.SetShape
|
||||
import software.amazon.smithy.model.shapes.StringShape
|
||||
import software.amazon.smithy.model.shapes.StructureShape
|
||||
import software.amazon.smithy.rust.codegen.lang.CargoDependency
|
||||
import software.amazon.smithy.rust.codegen.lang.RustType
|
||||
import software.amazon.smithy.rust.codegen.lang.RustWriter
|
||||
import software.amazon.smithy.rust.codegen.lang.docs
|
||||
import software.amazon.smithy.rust.codegen.lang.rustBlock
|
||||
import software.amazon.smithy.rust.codegen.smithy.RuntimeType
|
||||
import software.amazon.smithy.rust.codegen.util.lookup
|
||||
import software.amazon.smithy.rust.testutil.asSmithyModel
|
||||
import software.amazon.smithy.rust.testutil.compileAndRun
|
||||
import software.amazon.smithy.rust.testutil.compileAndTest
|
||||
import software.amazon.smithy.rust.testutil.shouldCompile
|
||||
import software.amazon.smithy.rust.testutil.shouldMatchResource
|
||||
import software.amazon.smithy.rust.testutil.shouldParseAsRust
|
||||
|
@ -78,4 +83,33 @@ class RustWriterTest {
|
|||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generate docs`() {
|
||||
val sut = RustWriter.forModule("lib")
|
||||
sut.docs(
|
||||
"""Top level module documentation
|
||||
|More docs
|
||||
|/* handle weird characters */
|
||||
|`a backtick`
|
||||
|[a link](asdf)
|
||||
""".trimMargin()
|
||||
)
|
||||
sut.rustBlock("fn main()") { }
|
||||
sut.compileAndTest()
|
||||
sut.toString() shouldContain "Top level module"
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `generate doc links`() {
|
||||
val model = """
|
||||
namespace test
|
||||
structure Foo {}
|
||||
""".asSmithyModel()
|
||||
val shape = model.lookup<StructureShape>("test#Foo")
|
||||
val symbol = testSymbolProvider(model).toSymbol(shape)
|
||||
val sut = RustWriter.forModule("lib")
|
||||
sut.docs("A link! \$D", symbol)
|
||||
sut.toString() shouldContain "/// A link! [`Foo`](crate::model::Foo)"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,8 +69,13 @@ fun RustWriter.compileAndTest(
|
|||
// TODO: if there are no dependencies, we can be a bit quicker
|
||||
val deps = this.dependencies.map { RustDependency.fromSymbolDependency(it) }.filterIsInstance<CargoDependency>()
|
||||
try {
|
||||
val module = if (this.namespace.contains("::")) {
|
||||
this.namespace.split("::")[1]
|
||||
} else {
|
||||
"lib"
|
||||
}
|
||||
val output = this.toString()
|
||||
.compileAndTest(deps.toSet(), module = this.namespace.split("::")[1], main = main, strict = clippy)
|
||||
.compileAndTest(deps.toSet(), module = module, main = main, strict = clippy)
|
||||
if (expectFailure) {
|
||||
println(this.toString())
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ val TestSymbolVisitorConfig = SymbolVisitorConfig(runtimeConfig = TestRuntimeCon
|
|||
fun testSymbolProvider(model: Model): SymbolProvider = RustCodegenPlugin.BaseSymbolProvider(model, TestSymbolVisitorConfig)
|
||||
|
||||
private const val SmithyVersion = "1.0"
|
||||
fun String.asSmithy(sourceLocation: String? = null): Model {
|
||||
fun String.asSmithyModel(sourceLocation: String? = null): Model {
|
||||
val processed = letIf(!this.startsWith("\$version")) { "\$version: ${SmithyVersion.dq()}\n$it" }
|
||||
return Model.assembler().discoverModels().addUnparsedModel(sourceLocation ?: "test.smithy", processed).assemble().unwrap()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue