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

use empty {} in place of empty body when parsing json (#551)

* use empty {} in place of empty body when parsing json

* add test
parent 22edad17
Loading
Loading
Loading
Loading
+27 −14
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ class JsonParserGenerator(
        "Peekable" to RuntimeType.std.member("iter::Peekable"),
        "skip_value" to smithyJson.member("deserialize::token::skip_value"),
        "Token" to smithyJson.member("deserialize::Token"),
        "or_empty" to orEmptyJson()
    )

    override fun payloadParser(member: MemberShape): RuntimeType {
@@ -87,7 +88,7 @@ class JsonParserGenerator(
            ) {
                rustTemplate(
                    """
                    let mut tokens_owned = #{json_token_iter}(input).peekable();
                    let mut tokens_owned = #{json_token_iter}(#{or_empty}(input)).peekable();
                    let tokens = &mut tokens_owned;
                    """,
                    *codegenScope
@@ -116,10 +117,9 @@ class JsonParserGenerator(
                "Builder" to outputShape.builderSymbol(symbolProvider),
                *codegenScope
            ) {
                rustBlock("if !input.is_empty()") {
                rustTemplate(
                    """
                        let mut tokens_owned = #{json_token_iter}(input).peekable();
                    let mut tokens_owned = #{json_token_iter}(#{or_empty}(input)).peekable();
                    let tokens = &mut tokens_owned;
                    #{expect_start_object}(tokens.next())?;
                    """,
@@ -127,7 +127,6 @@ class JsonParserGenerator(
                )
                deserializeStructInner(httpDocumentMembers)
                expectEndOfTokenStream()
                }
                rust("Ok(builder)")
            }
        }
@@ -146,7 +145,7 @@ class JsonParserGenerator(
            ) {
                rustTemplate(
                    """
                    let mut tokens_owned = #{json_token_iter}(input).peekable();
                    let mut tokens_owned = #{json_token_iter}(#{or_empty}(input)).peekable();
                    let tokens = &mut tokens_owned;
                    #{expect_start_object}(tokens.next())?;
                    """,
@@ -181,6 +180,20 @@ class JsonParserGenerator(
        }
    }

    private fun orEmptyJson(): RuntimeType = RuntimeType.forInlineFun("or_empty_doc", "json_deser") {
        it.rust(
            """
            pub fn or_empty_doc(data: &[u8]) -> &[u8] {
                if data.is_empty() {
                    b"{}"
                } else {
                    data
                }
            }
        """
        )
    }

    private fun RustWriter.expectEndOfTokenStream() {
        rustBlock("if tokens.next().is_some()") {
            rustTemplate(
+22 −0
Original line number Diff line number Diff line
@@ -91,9 +91,16 @@ class JsonParserGeneratorTest {
            top: Top
        }

        @error("client")
        structure Error {
            message: String,
            reason: String
        }

        @http(uri: "/top", method: "POST")
        operation Op {
            output: OpOutput,
            errors: [Error]
        }
    """.asSmithyModel()

@@ -108,6 +115,7 @@ class JsonParserGeneratorTest {
        val operationGenerator = parserGenerator.operationParser(model.lookup("test#Op"))
        val documentGenerator = parserGenerator.documentParser(model.lookup("test#Op"))
        val payloadGenerator = parserGenerator.payloadParser(model.lookup("test#OpOutput\$top"))
        val errorParser = parserGenerator.errorParser(model.lookup("test#Error"))

        val project = TestWorkspace.testProject(testSymbolProvider(model))
        project.lib { writer ->
@@ -132,6 +140,17 @@ class JsonParserGeneratorTest {
                assert_eq!(Some(45), top.extra);
                assert_eq!(Some("something".to_string()), top.field);
                assert_eq!(Some(Choice::Int(5)), top.choice);
                let output = ${writer.format(operationGenerator!!)}(b"", output::op_output::Builder::default()).unwrap().build();
                assert_eq!(output.top, None);


                // empty error
                let error_output = ${writer.format(errorParser!!)}(b"", error::error::Builder::default()).unwrap().build();
                assert_eq!(error_output.message, None);

                // error with message
                let error_output = ${writer.format(errorParser!!)}(br#"{"message": "hello"}"#, error::error::Builder::default()).unwrap().build();
                assert_eq!(error_output.message.expect("message should be set"), "hello");
                """
            )
        }
@@ -145,6 +164,9 @@ class JsonParserGeneratorTest {
        project.withModule(RustModule.default("output", public = true)) {
            model.lookup<OperationShape>("test#Op").outputShape(model).renderWithModelBuilder(model, symbolProvider, it)
        }
        project.withModule(RustModule.default("error", public = true)) {
            model.lookup<StructureShape>("test#Error").renderWithModelBuilder(model, symbolProvider, it)
        }
        println("file:///${project.baseDir}/src/json_deser.rs")
        println("file:///${project.baseDir}/src/lib.rs")
        println("file:///${project.baseDir}/src/model.rs")