Unverified Commit 6df93e09 authored by Russell Cohen's avatar Russell Cohen Committed by GitHub
Browse files

Parser Generator for XmlBindingTraits (#354)

* Parser Generator for XmlBindingTraits

This diff adds two components:
- `smithy-xml`: A scoped-reader XML abstraction on top of `xmlparser`
- `XmlBindingTraitParserGenerator`: A code generator for deserializing data targetted with the XmlBinding traits

This is not currently used outside of the tests, but this implementation passes all protocol tests expect for escaping (wiring is still WIP).

This commit also adds two inlinable abstractions for error parsing which can be swapped in at code generation time based on the service traits.

* Cleanup XmlError

* Add support for unescaping XML

* Back out changes to testutil

* Rename currentTarget to accum

* CR feedback

* Remove duplicate copyright & fix doc compilation

* CR feedback
parent 72a5c5b3
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -182,6 +182,9 @@ data class CargoDependency(
            "protocol-test-helpers", Local(runtimeConfig.relativePath), scope = DependencyScope.Dev
        )

        fun smithyXml(runtimeConfig: RuntimeConfig): CargoDependency =
            CargoDependency("${runtimeConfig.cratePrefix}-xml", Local(runtimeConfig.relativePath))

        val SerdeJson: CargoDependency =
            CargoDependency("serde_json", CratesIo("1"), features = listOf("float_roundtrip"))
        val Serde = CargoDependency("serde", CratesIo("1"), features = listOf("derive"))
+18 −0
Original line number Diff line number Diff line
@@ -73,6 +73,24 @@ fun <T : CodeWriter> T.rust(
    this.write(contents, *args)
}

/**
 * Sibling method to [rustBlock] that enables `#{variablename}` style templating
 */
fun <T : CodeWriter> T.rustBlockTemplate(
    @Language("Rust", prefix = "macro_rules! foo { () =>  {{ ", suffix = "}}}") contents: String,
    vararg ctx: Pair<String, Any>,
    block: T.() -> Unit
) {
    check(ctx.distinctBy { it.first.toLowerCase() }.size == ctx.size) { "Duplicate cased keys not supported" }
    this.pushState()
    this.putContext(ctx.toMap().mapKeys { (k, _) -> k.toLowerCase() })
    val header = contents.replace(Regex("""#\{([a-zA-Z_0-9]+)\}""")) { matchResult -> "#{${matchResult.groupValues[1].toLowerCase()}:T}" }
    this.openBlock("$header {")
    block(this)
    closeBlock("}")
    this.popState()
}

/**
 * API for templating long blocks of Rust
 *
+3 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import software.amazon.smithy.model.traits.HttpLabelTrait
import software.amazon.smithy.rust.codegen.rustlang.RustType
import software.amazon.smithy.rust.codegen.rustlang.RustWriter
import software.amazon.smithy.rust.codegen.rustlang.Writable
import software.amazon.smithy.rust.codegen.rustlang.stripOuter
import software.amazon.smithy.rust.codegen.smithy.traits.InputBodyTrait
import software.amazon.smithy.rust.codegen.smithy.traits.OutputBodyTrait
import software.amazon.smithy.rust.codegen.smithy.traits.SyntheticInputTrait
@@ -338,6 +339,8 @@ fun Symbol.isOptional(): Boolean = when (this.rustType()) {
    else -> false
}

fun Symbol.isBoxed(): Boolean = rustType().stripOuter<RustType.Option>() is RustType.Box

// Symbols should _always_ be created with a Rust type & shape attached
fun Symbol.rustType(): RustType = this.getProperty(RUST_TYPE_KEY, RustType::class.java).get()
fun Symbol.shape(): Shape = this.expectProperty(SHAPE_KEY, Shape::class.java)
+3 −3
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ class BuilderGenerator(
        val symbol = symbolProvider.toSymbol(shape)
        // TODO: figure out exactly what docs we want on a the builder module
        writer.docs("See #D", symbol)
        // check(writer.namespace == shape.builderSymbol(symbolProvider).namespace)
        val segments = shape.builderSymbol(symbolProvider).namespace.split("::")
        writer.withModule(segments.last()) {
            renderBuilder(this)
@@ -112,8 +111,9 @@ class BuilderGenerator(
                val memberName = symbolProvider.toMemberName(member)
                // All fields in the builder are optional
                val memberSymbol = symbolProvider.toSymbol(member).makeOptional()
                // TODO: should the builder members be public?
                write("$memberName: #T,", memberSymbol)
                // builder members are crate-public to enable using them
                // directly in serializers/deserializers
                write("pub(crate) $memberName: #T,", memberSymbol)
            }
        }

+610 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading