From 454a1174602d7a3193b101f27b8f617e522d434e Mon Sep 17 00:00:00 2001 From: ZackPierce Date: Mon, 25 Jun 2018 13:34:10 -0500 Subject: [PATCH] Handle IR changes to const enums for Rust; re-enable regression tests --- rust/car_example/src/main.rs | 6 ++ .../real_logic/sbe/generation/NamedToken.java | 10 ++- .../sbe/generation/rust/RustGenerator.java | 63 +++++++++++++------ .../generation/rust/RustGeneratorTest.java | 37 ++++++++++- .../test/resources/constant-enum-fields.xml | 34 ++++++++++ 5 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 sbe-tool/src/test/resources/constant-enum-fields.xml diff --git a/rust/car_example/src/main.rs b/rust/car_example/src/main.rs index 9248bd3c7f..5562a1eb5a 100644 --- a/rust/car_example/src/main.rs +++ b/rust/car_example/src/main.rs @@ -46,6 +46,7 @@ impl std::convert::From for IoError { fn decode_car_and_assert_expected_content(buffer: &[u8]) -> CodecResult<()> { let (h, dec_fields) = start_decoding_car(&buffer).header()?; assert_eq!(49u16, h.block_length); + assert_eq!(h.block_length as usize, ::std::mem::size_of::()); assert_eq!(1u16, h.template_id); assert_eq!(1u16, h.schema_id); assert_eq!(0u16, h.version); @@ -67,6 +68,11 @@ fn decode_car_and_assert_expected_content(buffer: &[u8]) -> CodecResult<()> { assert!(fields.extras.get_cruise_control()); assert!(fields.extras.get_sports_pack()); assert!(!fields.extras.get_sun_roof()); + assert_eq!(2000, fields.engine.capacity); + assert_eq!(4, fields.engine.num_cylinders); + assert_eq!(BoostType::NITROUS, fields.engine.booster.boost_type); + assert_eq!(200, fields.engine.booster.horse_power); + println!("Static-length fields all match the expected values"); let dec_perf_figures_header = match dec_fuel_figures_header.fuel_figures_individually()? { Either::Left(mut dec_ff_members) => { diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/NamedToken.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/NamedToken.java index 4ae6153adc..74686e5910 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/NamedToken.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/NamedToken.java @@ -43,10 +43,16 @@ public Token typeToken() return typeToken; } - public static List gatherNamedFieldTokens(final List fields) + public static List gatherNamedNonConstantFieldTokens(final List fields) { final List namedTokens = new ArrayList<>(); - forEachField(fields, (f, t) -> namedTokens.add(new NamedToken(f.name(), t))); + forEachField(fields, (f, t) -> + { + if (!f.isConstantEncoding()) + { + namedTokens.add(new NamedToken(f.name(), t)); + } + }); return namedTokens; } diff --git a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java index eddfc17fff..00ab79a2dc 100644 --- a/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java +++ b/sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java @@ -133,11 +133,7 @@ private static Optional generateFieldsRepresentatio final MessageComponents components, final OutputManager outputManager) throws IOException { - final List namedFieldTokens = NamedToken.gatherNamedFieldTokens(components.fields); - if (namedFieldTokens.isEmpty()) - { - return Optional.empty(); - } + final List namedFieldTokens = NamedToken.gatherNamedNonConstantFieldTokens(components.fields); final String representationStruct = messageTypeName + "Fields"; try (Writer writer = outputManager.createOutput(messageTypeName + " Fixed-size Fields")) @@ -149,12 +145,36 @@ private static Optional generateFieldsRepresentatio generateConstantAccessorImpl(writer, representationStruct, components.fields); } - final int numBytes = components.fields.stream() - .filter((t) -> !t.isConstantEncoding()) - .filter((t) -> t.signal() == ENCODING || t.signal() == BEGIN_ENUM || t.signal() == BEGIN_SET) - .mapToInt(Token::encodedLength) - .sum(); - + // Compute the total static size in bytes of the fields representation + int numBytes = 0; + for (int i = 0, size = components.fields.size(); i < size;) + { + final Token fieldToken = components.fields.get(i); + if (fieldToken.signal() == Signal.BEGIN_FIELD) + { + final int fieldEnd = i + fieldToken.componentTokenCount(); + if (!fieldToken.isConstantEncoding()) + { + for (int j = i; j < fieldEnd; j++) + { + final Token t = components.fields.get(j); + if (t.isConstantEncoding()) + { + continue; + } + if (t.signal() == ENCODING || t.signal() == BEGIN_ENUM || t.signal() == BEGIN_SET) + { + numBytes += t.encodedLength(); + } + } + } + i += fieldToken.componentTokenCount(); + } + else + { + throw new IllegalStateException("field tokens must include bounding BEGIN_FIELD and END_FIELD tokens"); + } + } return Optional.of(new FieldsRepresentationSummary(representationStruct, numBytes)); } @@ -847,7 +867,7 @@ static class GroupTreeNode this.blockLengthType = blockLengthType; this.blockLength = blockLength; this.rawFields = fields; - this.simpleNamedFields = NamedToken.gatherNamedFieldTokens(fields); + this.simpleNamedFields = NamedToken.gatherNamedNonConstantFieldTokens(fields); this.varData = varData; parent.ifPresent((p) -> p.addChild(this)); @@ -927,7 +947,7 @@ String generateVarDataEncoder( indent(writer, 3).append("return Err(CodecErr::SliceIsLongerThanAllowedBySchema)\n"); indent(writer, 2).append("}\n"); indent(writer, 2).append("// Write data length\n"); - indent(writer, 2, "%s.write_type::<%s>(&(l as %s), %s); // group length\n", + indent(writer, 2, "%s.write_type::<%s>(&(l as %s), %s)?; // group length\n", toScratchChain(groupDepth), rustTypeName(this.lengthType), rustTypeName(this.lengthType), this.lengthType.size()); indent(writer, 2).append(format("%s.write_slice_without_count::<%s>(s, %s)?;\n", @@ -1548,22 +1568,27 @@ private static void generateConstantAccessorImpl( case BEGIN_ENUM: final String enumType = formatTypeName(signalToken.applicableTypeName()); - String enumValue = null; + final String rawConstValueName = fieldToken.encoding().constValue().toString(); + final int indexOfDot = rawConstValueName.indexOf('.'); + final String constValueName = -1 == indexOfDot ? + rawConstValueName : rawConstValueName.substring(indexOfDot + 1); + boolean foundMatchingValueName = false; for (int j = i; j < unfilteredFields.size(); j++) { final Token searchAhead = unfilteredFields.get(j); - if (searchAhead.signal() == VALID_VALUE) + if (searchAhead.signal() == VALID_VALUE && searchAhead.name().equals(constValueName)) { - enumValue = searchAhead.name(); + foundMatchingValueName = true; break; } } - if (enumValue == null) + if (!foundMatchingValueName) { - throw new IllegalStateException("Found a constant enum field with incomplete token content"); + throw new IllegalStateException(format("Found a constant enum field that requested value %s, " + + "which is not an available enum option.", rawConstValueName)); } constantRustTypeName = enumType; - constantRustExpression = enumType + "::" + enumValue; + constantRustExpression = enumType + "::" + constValueName; break; case BEGIN_SET: diff --git a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustGeneratorTest.java b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustGeneratorTest.java index d462ede46c..c75c2aeeca 100644 --- a/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustGeneratorTest.java +++ b/sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustGeneratorTest.java @@ -133,6 +133,11 @@ public void fullGenerateBroadUseCase() throws IOException, InterruptedException { final String generatedRust = fullGenerateForResource(outputManager, "example-schema"); assertContainsSharedImports(generatedRust); + assertContains(generatedRust, + "pub fn car_fields(mut self) -> CodecResult<(&'d CarFields, CarFuelFiguresHeaderDecoder<'d>)> {\n" + + " let v = self.scratch.read_type::(49)?;\n" + + " Ok((v, CarFuelFiguresHeaderDecoder::wrap(self.scratch)))\n" + + " }"); final String expectedBooleanTypeDeclaration = "#[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]\n" + "#[repr(u8)]\n" + @@ -236,7 +241,6 @@ private void assertSchemaInterpretableAsRust(final String localResourceSchema) assertRustBuildable(rust, Optional.of(localResourceSchema)); } - @Ignore @Test public void checkValidRustFromAllExampleSchema() throws IOException, InterruptedException { @@ -267,7 +271,36 @@ public void checkValidRustFromAllExampleSchema() throws IOException, Interrupted } } - @Ignore + @Test + public void constantEnumFields() throws IOException, InterruptedException + { + final String rust = fullGenerateForResource(outputManager, "constant-enum-fields"); + assertContainsSharedImports(rust); + final String expectedCharTypeDeclaration = + "#[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]\n" + + "#[repr(i8)]\n" + + "pub enum Model {\n" + + " A = 65i8,\n" + + " B = 66i8,\n" + + " C = 67i8,\n" + + "}\n"; + assertContains(rust, expectedCharTypeDeclaration); + assertContains(rust, "pub struct ConstantEnumsFields {\n}"); + assertContains(rust, "impl ConstantEnumsFields {"); + assertContains(rust, " pub fn c() -> Model {\n" + + " Model::C\n }"); + assertContains(rust, "impl ConstantEnumsFMember {"); + assertContains(rust, " pub fn k() -> Model {\n" + + " Model::C\n }"); + assertContains(rust, + "pub fn constant_enums_fields(mut self) -> " + + "CodecResult<(&'d ConstantEnumsFields, ConstantEnumsFHeaderDecoder<'d>)> {\n" + + " let v = self.scratch.read_type::(0)?;\n" + + " Ok((v, ConstantEnumsFHeaderDecoder::wrap(self.scratch)))\n" + + " }"); + assertRustBuildable(rust, Optional.of("constant-enum-fields")); + } + @Test public void constantFieldsCase() throws IOException, InterruptedException { diff --git a/sbe-tool/src/test/resources/constant-enum-fields.xml b/sbe-tool/src/test/resources/constant-enum-fields.xml new file mode 100644 index 0000000000..1cff1926d2 --- /dev/null +++ b/sbe-tool/src/test/resources/constant-enum-fields.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + A + B + C + + + + + + + + +