Skip to content

Commit 048c7e6

Browse files
committed
Add support for set helper methods; constant-presence primitives in fields, composites, and groups
1 parent 882789a commit 048c7e6

File tree

4 files changed

+226
-36
lines changed

4 files changed

+226
-36
lines changed

rust/car_example/src/main.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ fn decode_car_and_assert_expected_content(buffer: &[u8]) -> CodecResult<()> {
5151
assert_eq!(0u16, h.version);
5252
println!("Header read");
5353

54+
assert_eq!(Model::C, CarFields::discounted_model());
55+
assert_eq!(9000u16, Engine::max_rpm());
56+
assert_eq!("Petrol", Engine::fuel());
5457

5558
let mut found_fuel_figures = Vec::<FuelFigure>::with_capacity(EXPECTED_FUEL_FIGURES.len());
5659

@@ -60,6 +63,10 @@ fn decode_car_and_assert_expected_content(buffer: &[u8]) -> CodecResult<()> {
6063
assert_eq!(BooleanType::T, fields.available);
6164
assert_eq!([97_i8, 98, 99, 100, 101, 102], fields.vehicle_code); // abcdef
6265
assert_eq!([0_u32, 1, 2, 3, 4], fields.some_numbers);
66+
assert_eq!(6, fields.extras.0);
67+
assert!(fields.extras.get_cruise_control());
68+
assert!(fields.extras.get_sports_pack());
69+
assert!(!fields.extras.get_sun_roof());
6370

6471
let dec_perf_figures_header = match dec_fuel_figures_header.fuel_figures_individually()? {
6572
Either::Left(mut dec_ff_members) => {
@@ -153,7 +160,10 @@ fn encode_car_from_scratch() -> CodecResult<Vec<u8>> {
153160
fields.code = Model::A;
154161
fields.vehicle_code = [97_i8, 98, 99, 100, 101, 102]; // abcdef
155162
fields.some_numbers = [0_u32, 1, 2, 3, 4];
156-
fields.extras = OptionalExtras(6);
163+
fields.extras = OptionalExtras::new();
164+
fields.extras.set_cruise_control(true)
165+
.set_sports_pack(true)
166+
.set_sun_roof(false);
157167
fields.engine = Engine {
158168
capacity: 2000,
159169
num_cylinders: 4,

sbe-tool/src/main/java/uk/co/real_logic/sbe/generation/rust/RustGenerator.java

Lines changed: 109 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ private void generateGroupFieldRepresentations(
9292
for (final GroupTreeNode node : groupTree)
9393
{
9494
appendStructHeader(appendable, node.contextualName + "Member", true);
95-
appendStructFields(appendable, node.fields);
95+
appendStructFields(appendable, node.simpleNamedFields);
9696
appendable.append("}\n");
9797

98+
generateConstantAccessorImpl(appendable, node.contextualName + "Member", node.rawFields);
99+
98100
generateGroupFieldRepresentations(appendable, node.groups);
99101
}
100102

@@ -128,7 +130,8 @@ private static Optional<FieldsRepresentationSummary> generateFieldsRepresentatio
128130
appendStructHeader(writer, representationStruct, true);
129131
appendStructFields(writer, namedFieldTokens);
130132
writer.append("}\n");
131-
// TODO - also implement constants
133+
134+
generateConstantAccessorImpl(writer, representationStruct, components.fields);
132135
}
133136
final int numBytes = components.fields.stream()
134137
.filter(t -> !t.isConstantEncoding())
@@ -161,8 +164,38 @@ private static void generateSingleBitSet(final List<Token> tokens, final OutputM
161164
writer.append("#[repr(C,packed)]\n");
162165
final String rustPrimitiveType = rustTypeName(beginToken.encoding().primitiveType());
163166
writer.append(format("pub struct %s(pub %s);%n", setType, rustPrimitiveType));
167+
writer.append(format("impl %s {\n", setType));
168+
indent(writer, 1, "pub fn new() -> Self {\n");
169+
indent(writer, 2, "%s(0)\n", setType);
170+
indent(writer, 1, "}\n");
164171

165-
// TODO - implementation to extract named choices with methods
172+
indent(writer, 1, "pub fn clear(&mut self) -> &mut Self {\n");
173+
indent(writer, 2, "self.0 = 0;\n");
174+
indent(writer, 2, "self\n");
175+
indent(writer, 1, "}\n");
176+
for (final Token token : tokens)
177+
{
178+
if (Signal.CHOICE != token.signal())
179+
{
180+
continue;
181+
}
182+
final String choiceName = formatMethodName(token.name());
183+
final Encoding encoding = token.encoding();
184+
final String choiceBitIndex = encoding.constValue().toString();
185+
indent(writer, 1, "pub fn get_%s(&self) -> bool {\n", choiceName);
186+
indent(writer, 2, "0 != self.0 & (1 << %s)\n", choiceBitIndex);
187+
indent(writer, 1, "}\n", choiceName);
188+
189+
indent(writer, 1, "pub fn set_%s(&mut self, value: bool) -> &mut Self {\n", choiceName);
190+
indent(writer, 2, "self.0 = if value {\n", choiceBitIndex);
191+
indent(writer, 3, "self.0 | (1 << %s)\n", choiceBitIndex);
192+
indent(writer, 2, "} else {\n");
193+
indent(writer, 3, "self.0 & !(1 << %s)\n", choiceBitIndex);
194+
indent(writer, 2, "};\n");
195+
indent(writer, 2, "self\n");
196+
indent(writer, 1, "}\n", choiceName);
197+
}
198+
writer.append("}\n");
166199
}
167200

168201
}
@@ -695,7 +728,6 @@ private static List<GroupTreeNode> buildGroupTrees(final String parentTypeName,
695728

696729
final List<Token> fields = new ArrayList<>();
697730
i = collectFields(groupsTokens, i, fields);
698-
final List<NamedToken> namedFields = NamedToken.gatherNamedFieldTokens(fields);
699731

700732
final List<Token> childGroups = new ArrayList<>();
701733
i = collectGroups(groupsTokens, i, childGroups);
@@ -711,7 +743,7 @@ private static List<GroupTreeNode> buildGroupTrees(final String parentTypeName,
711743
numInGroupType,
712744
blockLengthType,
713745
blockLength,
714-
namedFields,
746+
fields,
715747
varDataSummaries
716748
);
717749
groups.add(node);
@@ -746,7 +778,8 @@ static class GroupTreeNode
746778
final PrimitiveType numInGroupType;
747779
final PrimitiveType blockLengthType;
748780
final int blockLength;
749-
final List<NamedToken> fields;
781+
final List<Token> rawFields;
782+
final List<NamedToken> simpleNamedFields;
750783
final List<GroupTreeNode> groups = new ArrayList<>();
751784
final List<VarDataSummary> varData;
752785

@@ -756,7 +789,7 @@ static class GroupTreeNode
756789
final PrimitiveType numInGroupType,
757790
final PrimitiveType blockLengthType,
758791
final int blockLength,
759-
final List<NamedToken> fields,
792+
final List<Token> fields,
760793
final List<VarDataSummary> varData)
761794
{
762795
this.parent = parent;
@@ -765,7 +798,8 @@ static class GroupTreeNode
765798
this.numInGroupType = numInGroupType;
766799
this.blockLengthType = blockLengthType;
767800
this.blockLength = blockLength;
768-
this.fields = fields;
801+
this.rawFields = fields;
802+
this.simpleNamedFields = NamedToken.gatherNamedFieldTokens(fields);
769803
this.varData = varData;
770804
parent.ifPresent(p -> p.addChild(this));
771805
}
@@ -1231,10 +1265,7 @@ private static void generateSingleComposite(final List<Token> tokens, final Outp
12311265
appendStructFields(writer, splitTokens.nonConstantEncodingTokens);
12321266
writer.append("}\n");
12331267

1234-
if (!splitTokens.constantEncodingTokens.isEmpty())
1235-
{
1236-
generateCompositeImplForConstants(formattedTypeName, writer, splitTokens.constantEncodingTokens);
1237-
}
1268+
generateConstantAccessorImpl(writer, formattedTypeName, getMessageBody(tokens));
12381269
}
12391270
}
12401271

@@ -1351,20 +1382,42 @@ private static String getRustTypeForPrimitivePossiblyArray(final Token encodingT
13511382
return rustType;
13521383
}
13531384

1354-
private static void generateCompositeImplForConstants(final String formattedTypeName,
1355-
final Writer writer,
1356-
final List<Token> deferredConstantTokens) throws IOException
1385+
private static void generateConstantAccessorImpl(final Appendable writer, final String formattedTypeName,
1386+
final List<Token> unfilteredFields) throws IOException
13571387
{
13581388
writer.append(format("%nimpl %s {%n", formattedTypeName));
1359-
for (final Token constantToken : deferredConstantTokens)
1389+
for (int i = 0; i < unfilteredFields.size(); )
13601390
{
1391+
final Token fieldToken = unfilteredFields.get(i);
1392+
final String name = fieldToken.name();
1393+
final int componentTokenCount = fieldToken.componentTokenCount();
1394+
final Token signalToken;
1395+
if (fieldToken.signal() == BEGIN_FIELD)
1396+
{
1397+
if (i > unfilteredFields.size() - 1)
1398+
{
1399+
throw new ArrayIndexOutOfBoundsException("BEGIN_FIELD token should be followed by content tokens");
1400+
}
1401+
signalToken = unfilteredFields.get(i + 1);
1402+
} else
1403+
{
1404+
signalToken = fieldToken;
1405+
}
1406+
1407+
// Either the field must be marked directly as constant
1408+
// or it must wrap something that is fully constant
1409+
if (!(fieldToken.isConstantEncoding() || signalToken.isConstantEncoding()))
1410+
{
1411+
i += componentTokenCount;
1412+
continue;
1413+
}
13611414
final String constantRustTypeName;
13621415
final String constantRustExpression;
1363-
switch (constantToken.signal())
1416+
switch (signalToken.signal())
13641417
{
13651418
case ENCODING:
1366-
final String rawValue = constantToken.encoding().constValue().toString();
1367-
if (constantToken.encoding().primitiveType() == PrimitiveType.CHAR)
1419+
final String rawValue = signalToken.encoding().constValue().toString();
1420+
if (signalToken.encoding().primitiveType() == PrimitiveType.CHAR)
13681421
{
13691422
// Special case string handling
13701423
constantRustTypeName = "&'static str";
@@ -1373,32 +1426,54 @@ private static void generateCompositeImplForConstants(final String formattedType
13731426

13741427
} else
13751428
{
1376-
final String constantRustPrimitiveType = RustUtil.rustTypeName(constantToken.encoding()
1377-
.primitiveType());
1378-
constantRustTypeName = getRustTypeForPrimitivePossiblyArray(constantToken,
1379-
constantRustPrimitiveType);
1380-
constantRustExpression = generateRustLiteral(constantToken.encoding().primitiveType(),
1381-
rawValue);
1429+
final String constantRustPrimitiveType = RustUtil.rustTypeName(signalToken.encoding()
1430+
.primitiveType());
1431+
constantRustTypeName = getRustTypeForPrimitivePossiblyArray(signalToken,
1432+
constantRustPrimitiveType);
1433+
constantRustExpression = generateRustLiteral(signalToken.encoding().primitiveType(),
1434+
rawValue);
13821435
}
13831436
break;
13841437
case BEGIN_ENUM:
1385-
// TODO - implement constant enums
1386-
throw new IllegalStateException(format("Unsupported constant presence enum or bitset property " +
1387-
"%s", constantToken.toString()));
1438+
final String enumType = formatTypeName(signalToken.applicableTypeName());
1439+
String enumValue = null;
1440+
for (int j = i; j < unfilteredFields.size(); j++)
1441+
{
1442+
final Token searchAhead = unfilteredFields.get(j);
1443+
if (searchAhead.signal() == VALID_VALUE)
1444+
{
1445+
enumValue = searchAhead.name();
1446+
break;
1447+
}
1448+
}
1449+
if (enumValue == null)
1450+
{
1451+
throw new IllegalStateException("Found a constant enum field with incomplete token content");
1452+
}
1453+
constantRustTypeName = enumType;
1454+
constantRustExpression = enumType + "::" + enumValue;
1455+
break;
13881456
case BEGIN_SET:
13891457
case BEGIN_COMPOSITE:
13901458
default:
13911459
throw new IllegalStateException(format("Unsupported constant presence property " +
1392-
"%s", constantToken.toString()));
1460+
"%s", fieldToken.toString()));
13931461
}
1394-
writer.append("\n").append(INDENT).append("#[inline]\n").append(INDENT);
1395-
writer.append(format("pub fn %s() -> %s {%n", formatMethodName(constantToken.name()),
1396-
constantRustTypeName));
1397-
indent(writer, 2).append(constantRustExpression).append("\n");
1398-
indent(writer).append("}\n");
1462+
appendConstAccessor(writer, name, constantRustTypeName, constantRustExpression);
1463+
i += componentTokenCount;
13991464
}
14001465

14011466
writer.append("}\n");
14021467
}
14031468

1469+
private static void appendConstAccessor(final Appendable writer, final String name,
1470+
final String rustTypeName, final String rustExpression) throws IOException
1471+
{
1472+
writer.append("\n").append(INDENT).append("#[inline]\n").append(INDENT);
1473+
writer.append(format("pub fn %s() -> %s {%n", formatMethodName(name),
1474+
rustTypeName));
1475+
indent(writer, 2).append(rustExpression).append("\n");
1476+
indent(writer).append("}\n");
1477+
}
1478+
14041479
}

sbe-tool/src/test/java/uk/co/real_logic/sbe/generation/rust/RustGeneratorTest.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ public void fullGenerateBroadUseCase() throws IOException, InterruptedException
143143
" C = 67i8,\n" +
144144
"}\n";
145145
assertTrue(generatedRust.contains(expectedCharTypeDeclaration));
146-
assertRustBuildable(generatedRust, Optional.of(BROAD_USE_CASES_SCHEMA));
146+
assertRustBuildable(generatedRust, Optional.of("example-schema"));
147147
}
148148

149149
private File writeCargoFolderWrapper(final String name, final String generatedRust, final File folder) throws
@@ -236,6 +236,7 @@ public void checkValidRustFromAllExampleSchema() throws IOException, Interrupted
236236
"example-schema",
237237
"FixBinary",
238238
"group-with-data-schema",
239+
"group-with-constant-fields",
239240
"issue435",
240241
"message-block-length-test",
241242
NESTED_GROUP_SCHEMA,
@@ -248,4 +249,58 @@ public void checkValidRustFromAllExampleSchema() throws IOException, Interrupted
248249

249250

250251
}
252+
253+
@Test
254+
public void constantFieldsCase() throws IOException, InterruptedException
255+
{
256+
final String rust = fullGenerateForResource(outputManager, "group-with-constant-fields");
257+
assertContainsSharedImports(rust);
258+
final String expectedCharTypeDeclaration =
259+
"#[derive(Clone,Copy,Debug,PartialEq,Eq,PartialOrd,Ord,Hash)]\n" +
260+
"#[repr(i8)]\n" +
261+
"pub enum Model {\n" +
262+
" A = 65i8,\n" +
263+
" B = 66i8,\n" +
264+
" C = 67i8,\n" +
265+
"}\n";
266+
assertContains(rust, expectedCharTypeDeclaration);
267+
final String expectedComposite =
268+
"pub struct CompositeWithConst {\n" +
269+
" pub w:u8,\n" +
270+
"}";
271+
assertContains(rust, expectedComposite);
272+
assertContains(rust, "impl CompositeWithConst {");
273+
assertContains(rust, " pub fn x() -> u8 {\n" +
274+
" 250u8\n }");
275+
assertContains(rust, " pub fn y() -> u16 {\n" +
276+
" 9000u16\n }");
277+
assertContains(rust, "pub struct ConstantsGaloreFields {\n" +
278+
" pub a:u8,\n" +
279+
" pub e:CompositeWithConst,\n}");
280+
assertContains(rust, "impl ConstantsGaloreFields {");
281+
assertContains(rust, " pub fn b() -> u16 {\n" +
282+
" 9000u16\n }");
283+
assertContains(rust, " pub fn c() -> Model {\n" +
284+
" Model::C\n }");
285+
assertContains(rust, " pub fn d() -> u16 {\n" +
286+
" 9000u16\n }");
287+
assertContains(rust, "pub struct ConstantsGaloreFMember {\n" +
288+
" pub g:u8,\n" +
289+
" pub h:CompositeWithConst,\n}");
290+
assertContains(rust, "impl ConstantsGaloreFMember {");
291+
assertContains(rust, " pub fn i() -> u16 {\n" +
292+
" 9000u16\n }");
293+
assertContains(rust, " pub fn j() -> u16 {\n" +
294+
" 9000u16\n }");
295+
assertContains(rust, " pub fn k() -> Model {\n" +
296+
" Model::C\n }");
297+
assertContains(rust, " pub fn l() -> &'static str {\n" +
298+
" \"Huzzah\"\n }");
299+
assertRustBuildable(rust, Optional.of("group-with-constant-fields"));
300+
}
301+
302+
private static void assertContains(final String haystack, final String needle)
303+
{
304+
assertTrue(String.format("Did not contain %s", needle), haystack.contains(needle));
305+
}
251306
}

0 commit comments

Comments
 (0)