Loading codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +27 −14 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 Loading Loading @@ -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())?; """, Loading @@ -127,7 +127,6 @@ class JsonParserGenerator( ) deserializeStructInner(httpDocumentMembers) expectEndOfTokenStream() } rust("Ok(builder)") } } Loading @@ -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())?; """, Loading Loading @@ -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( Loading codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGeneratorTest.kt +22 −0 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -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 -> Loading @@ -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"); """ ) } Loading @@ -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") Loading Loading
codegen/src/main/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGenerator.kt +27 −14 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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 Loading Loading @@ -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())?; """, Loading @@ -127,7 +127,6 @@ class JsonParserGenerator( ) deserializeStructInner(httpDocumentMembers) expectEndOfTokenStream() } rust("Ok(builder)") } } Loading @@ -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())?; """, Loading Loading @@ -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( Loading
codegen/src/test/kotlin/software/amazon/smithy/rust/codegen/smithy/protocols/parse/JsonParserGeneratorTest.kt +22 −0 Original line number Diff line number Diff line Loading @@ -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() Loading @@ -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 -> Loading @@ -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"); """ ) } Loading @@ -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") Loading