Skip to content

Commit 1a125f1

Browse files
author
Leonardo Arcari
committed
feat(rust): recursively call nullify_optional_fields on composites
1 parent 50fab5b commit 1a125f1

5 files changed

Lines changed: 100 additions & 48 deletions

File tree

rust/tests/optional_field_nullify_test.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -387,33 +387,46 @@ fn nullify_optional_fields_sets_optional_enum_field_to_null_value() -> TestResul
387387
control_encoder.optional_enum_opt(Some(OenEnumType::One));
388388
// This field is required, even if its enum type encoding is optional.
389389
control_encoder.required_enum_from_optional_type(OenOptionalEncodingEnumType::Alpha);
390+
let mut control_composite = control_encoder.optional_composite_encoder();
391+
control_composite.optional_counter_opt(Some(77));
392+
let _control_encoder = control_composite.parent()?;
390393

391394
let control_decoder = decode_optional_enum_nullify(control_buffer.as_slice());
392395
assert_eq!(OenEnumType::One, control_decoder.optional_enum());
393396
assert_eq!(
394397
OenOptionalEncodingEnumType::Alpha,
395398
control_decoder.required_enum_from_optional_type()
396399
);
400+
let control_composite = control_decoder.optional_composite_decoder();
401+
assert_eq!(Some(77), control_composite.optional_counter());
397402

398403
let mut none_buffer = vec![0u8; 256];
399404
let mut none_encoder = create_optional_enum_nullify_encoder(&mut none_buffer);
400405
none_encoder.optional_enum_opt(None);
401406
none_encoder.required_enum_from_optional_type(OenOptionalEncodingEnumType::Beta);
407+
let mut none_composite = none_encoder.optional_composite_encoder();
408+
none_composite.optional_counter_opt(None);
409+
let _none_encoder = none_composite.parent()?;
402410

403411
let none_decoder = decode_optional_enum_nullify(none_buffer.as_slice());
404412
assert_eq!(OenEnumType::NullVal, none_decoder.optional_enum());
405413
assert_eq!(
406414
OenOptionalEncodingEnumType::Beta,
407415
none_decoder.required_enum_from_optional_type()
408416
);
417+
let none_composite = none_decoder.optional_composite_decoder();
418+
assert_eq!(None, none_composite.optional_counter());
409419

410420
let mut nullified_buffer = vec![0u8; 256];
411421
let mut nullified_encoder = create_optional_enum_nullify_encoder(&mut nullified_buffer);
412422
nullified_encoder.optional_enum_opt(Some(OenEnumType::Two));
413423
nullified_encoder.required_enum_from_optional_type(OenOptionalEncodingEnumType::Beta);
424+
let mut nullified_composite = nullified_encoder.optional_composite_encoder();
425+
nullified_composite.optional_counter_opt(Some(88));
426+
nullified_encoder = nullified_composite.parent()?;
414427
// This is the behavior under test: nullify should route through *_opt(None)
415-
// only for field-optional members, so `optional_enum` becomes NullVal while
416-
// the required field remains unchanged even if its enum type encoding is optional.
428+
// and recurse into composite fields, so `optional_enum` and composite optional
429+
// members become null while required fields remain unchanged.
417430
nullified_encoder.nullify_optional_fields();
418431

419432
let nullified_decoder = decode_optional_enum_nullify(nullified_buffer.as_slice());
@@ -422,6 +435,8 @@ fn nullify_optional_fields_sets_optional_enum_field_to_null_value() -> TestResul
422435
OenOptionalEncodingEnumType::Beta,
423436
nullified_decoder.required_enum_from_optional_type()
424437
);
438+
let nullified_composite = nullified_decoder.optional_composite_decoder();
439+
assert_eq!(None, nullified_composite.optional_counter());
425440

426441
Ok(())
427442
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ void generate(
7272

7373
if (codecType == Encoder)
7474
{
75-
final List<String> optionalFields = RustGenerator.getNullifyOptionalFields(fields);
76-
RustGenerator.appendImplEncoderTrait(sb, msgTypeName, optionalFields);
75+
final RustGenerator.NullifyTargets nullifyTargets = RustGenerator.getNullifyTargets(fields);
76+
RustGenerator.appendImplEncoderTrait(sb, msgTypeName, nullifyTargets);
7777
}
7878
else
7979
{

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

Lines changed: 75 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,17 @@ interface ParentDef
8282
SubGroup addSubGroup(String name, int level, Token groupToken);
8383
}
8484

85+
static final class NullifyTargets
86+
{
87+
final List<String> optionalFields = new ArrayList<>();
88+
final List<String> compositeFieldEncoders = new ArrayList<>();
89+
90+
boolean isEmpty()
91+
{
92+
return optionalFields.isEmpty() && compositeFieldEncoders.isEmpty();
93+
}
94+
}
95+
8596
private final Ir ir;
8697
private final RustOutputManager outputManager;
8798
private final String crateVersion;
@@ -224,9 +235,9 @@ static String getBufOffset(final Token token)
224235
return "offset";
225236
}
226237

227-
static List<String> getNullifyOptionalFields(final List<Token> tokens)
238+
static NullifyTargets getNullifyTargets(final List<Token> tokens)
228239
{
229-
final List<String> optionalFields = new ArrayList<>();
240+
final NullifyTargets targets = new NullifyTargets();
230241
Generators.forEachField(
231242
tokens,
232243
(fieldToken, typeToken) ->
@@ -237,16 +248,20 @@ static List<String> getNullifyOptionalFields(final List<Token> tokens)
237248
{
238249
if (isOptionalPrimitiveScalar(typeToken))
239250
{
240-
optionalFields.add(fieldName);
251+
targets.optionalFields.add(fieldName);
241252
}
242253
}
243254
else if (signal == BEGIN_ENUM &&
244255
isOptionalEnum(typeToken, fieldToken))
245256
{
246-
optionalFields.add(fieldName);
257+
targets.optionalFields.add(fieldName);
258+
}
259+
else if (signal == Signal.BEGIN_COMPOSITE)
260+
{
261+
targets.compositeFieldEncoders.add(compositeEncoderAccessorName(fieldToken.name()));
247262
}
248263
});
249-
return optionalFields;
264+
return targets;
250265
}
251266

252267
static void generateEncoderFields(
@@ -297,17 +312,53 @@ static void generateEncoderFields(
297312
static void generateNullifyOptionalFieldsMethod(
298313
final Appendable out,
299314
final int level,
300-
final List<String> optionalFields) throws IOException
315+
final NullifyTargets targets) throws IOException
301316
{
302317
indent(out, level, "/// Set all optional fields to their null values.\n");
303318
indent(out, level, "#[inline]\n");
304319
indent(out, level, "pub fn nullify_optional_fields(&mut self) -> &mut Self {\n");
305-
for (final String field : optionalFields)
320+
appendNullifyOptionalFieldsBody(out, level + 1, targets);
321+
indent(out, level + 1, "self\n");
322+
indent(out, level, "}\n\n");
323+
}
324+
325+
private static void appendNullifyOptionalFieldsBody(
326+
final Appendable out,
327+
final int level,
328+
final NullifyTargets targets) throws IOException
329+
{
330+
for (final String field : targets.optionalFields)
306331
{
307-
indent(out, level + 1, "self.%s_opt(None);\n", field);
332+
indent(out, level, "self.%s_opt(None);\n", field);
308333
}
334+
335+
for (final String compositeFieldEncoder : targets.compositeFieldEncoders)
336+
{
337+
indent(out, level, "{\n");
338+
indent(out, level + 1, "let mut composite_encoder = core::mem::take(self).%s();\n", compositeFieldEncoder);
339+
indent(out, level + 1, "composite_encoder.nullify_optional_fields();\n");
340+
indent(out, level + 1, "*self = composite_encoder.parent().expect(\"parent missing\");\n");
341+
indent(out, level, "}\n");
342+
}
343+
}
344+
345+
private static void appendTraitNullifyOptionalFieldsMethod(
346+
final Appendable out,
347+
final int level,
348+
final NullifyTargets targets) throws IOException
349+
{
350+
if (targets.isEmpty())
351+
{
352+
return;
353+
}
354+
355+
indent(out, 0, "\n");
356+
indent(out, level, "/// Set all optional fields to their 'null' values.\n");
357+
indent(out, level, "#[inline]\n");
358+
indent(out, level, "fn nullify_optional_fields(&mut self) -> &mut Self {\n");
359+
appendNullifyOptionalFieldsBody(out, level + 1, targets);
309360
indent(out, level + 1, "self\n");
310-
indent(out, level, "}\n\n");
361+
indent(out, level, "}\n");
311362
}
312363

313364
private static boolean isOptionalPrimitiveScalar(final Token typeToken)
@@ -327,6 +378,11 @@ private static String rustEnumType(final Token typeToken)
327378
return format("%s::%s", toLowerSnakeCase(enumTypeName), formatStructName(enumTypeName));
328379
}
329380

381+
private static String compositeEncoderAccessorName(final String name)
382+
{
383+
return toLowerSnakeCase(encoderName(name));
384+
}
385+
330386
static void generateEncoderGroups(
331387
final StringBuilder sb,
332388
final List<Token> tokens,
@@ -734,7 +790,7 @@ private static void generateCompositeEncoder(
734790
final Token typeToken,
735791
final String name) throws IOException
736792
{
737-
final String encoderName = toLowerSnakeCase(encoderName(name));
793+
final String encoderName = compositeEncoderAccessorName(name);
738794
final String encoderTypeName = format("%s::%s",
739795
codecModName(typeToken.referencedName() == null ? typeToken.name() : typeToken.referencedName()),
740796
encoderName(formatStructName(typeToken.applicableTypeName())));
@@ -1381,7 +1437,7 @@ private static void generateSingleBitSet(
13811437
static void appendImplEncoderTrait(
13821438
final Appendable out,
13831439
final String typeName,
1384-
final List<String> optionalFields) throws IOException
1440+
final NullifyTargets targets) throws IOException
13851441
{
13861442
indent(out, 1, "impl<%s> %s for %s {\n", BUF_LIFETIME, withBufLifetime("Writer"), withBufLifetime(typeName));
13871443
indent(out, 2, "#[inline]\n");
@@ -1401,19 +1457,7 @@ static void appendImplEncoderTrait(
14011457
indent(out, 3, "self.limit = limit;\n");
14021458
indent(out, 2, "}\n");
14031459

1404-
if (!optionalFields.isEmpty())
1405-
{
1406-
indent(out, 0, "\n");
1407-
indent(out, 2, "/// Set all optional fields to their 'null' values.\n");
1408-
indent(out, 2, "#[inline]\n");
1409-
indent(out, 2, "fn nullify_optional_fields(&mut self) -> &mut Self {\n");
1410-
for (final String field : optionalFields)
1411-
{
1412-
indent(out, 3, "self.%s_opt(None);\n", field);
1413-
}
1414-
indent(out, 3, "self\n");
1415-
indent(out, 2, "}\n");
1416-
}
1460+
appendTraitNullifyOptionalFieldsMethod(out, 2, targets);
14171461

14181462
indent(out, 1, "}\n\n");
14191463
}
@@ -1686,7 +1730,7 @@ static void appendImplEncoderForComposite(
16861730
final Appendable out,
16871731
final int level,
16881732
final String name,
1689-
final List<String> optionalFields) throws IOException
1733+
final NullifyTargets targets) throws IOException
16901734
{
16911735
appendImplWriterForComposite(out, level, name);
16921736

@@ -1702,19 +1746,7 @@ static void appendImplEncoderForComposite(
17021746
indent(out, level + 2, "self.parent.as_mut().expect(\"parent missing\").set_limit(limit);\n");
17031747
indent(out, level + 1, "}\n");
17041748

1705-
if (!optionalFields.isEmpty())
1706-
{
1707-
indent(out, 0, "\n");
1708-
indent(out, 2, "/// Set all optional fields to their 'null' values.\n");
1709-
indent(out, 2, "#[inline]\n");
1710-
indent(out, 2, "fn nullify_optional_fields(&mut self) -> &mut Self {\n");
1711-
for (final String field : optionalFields)
1712-
{
1713-
indent(out, 3, "self.%s_opt(None);\n", field);
1714-
}
1715-
indent(out, 3, "self\n");
1716-
indent(out, 2, "}\n");
1717-
}
1749+
appendTraitNullifyOptionalFieldsMethod(out, 2, targets);
17181750

17191751
indent(out, level, "}\n\n");
17201752
}
@@ -1800,7 +1832,7 @@ private static void generateCompositeEncoder(
18001832
indent(out, 3, "self.parent.take().ok_or(SbeErr::ParentNotSet)\n");
18011833
indent(out, 2, "}\n\n");
18021834

1803-
final List<String> optionalFields = new ArrayList<>();
1835+
final NullifyTargets nullifyTargets = new NullifyTargets();
18041836
for (int i = 1, end = tokens.size() - 1; i < end; )
18051837
{
18061838
final Token encodingToken = tokens.get(i);
@@ -1814,22 +1846,23 @@ private static void generateCompositeEncoder(
18141846
{
18151847
generateOptionalPrimitiveEncoder(sb, 2, encodingToken, encodingToken.name(),
18161848
encodingToken.encoding());
1817-
optionalFields.add(formatFunctionName(encodingToken.name()));
1849+
nullifyTargets.optionalFields.add(formatFunctionName(encodingToken.name()));
18181850
}
18191851
break;
18201852
case BEGIN_ENUM:
18211853
generateEnumEncoder(sb, 2, encodingToken, encodingToken, encodingToken.name());
18221854
if (isOptionalEnum(encodingToken, encodingToken))
18231855
{
18241856
generateOptionalEnumEncoder(sb, 2, encodingToken, encodingToken.name());
1825-
optionalFields.add(formatFunctionName(encodingToken.name()));
1857+
nullifyTargets.optionalFields.add(formatFunctionName(encodingToken.name()));
18261858
}
18271859
break;
18281860
case BEGIN_SET:
18291861
generateBitSetEncoder(sb, 2, encodingToken, encodingToken.name());
18301862
break;
18311863
case BEGIN_COMPOSITE:
18321864
generateCompositeEncoder(sb, 2, encodingToken, encodingToken.name());
1865+
nullifyTargets.compositeFieldEncoders.add(compositeEncoderAccessorName(encodingToken.name()));
18331866
break;
18341867
default:
18351868
break;
@@ -1839,7 +1872,7 @@ private static void generateCompositeEncoder(
18391872
i += encodingToken.componentTokenCount();
18401873
}
18411874

1842-
generateNullifyOptionalFieldsMethod(out, 2, optionalFields);
1875+
generateNullifyOptionalFieldsMethod(out, 2, nullifyTargets);
18431876
indent(out, 1, "}\n"); // end impl
18441877
indent(out, 0, "} // end encoder mod \n");
18451878
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@ void generateEncoder(
7272
indent(sb, level, "initial_limit: usize,\n");
7373
indent(sb, level - 1, "}\n\n");
7474

75-
final List<String> optionalFields = RustGenerator.getNullifyOptionalFields(fields);
76-
RustGenerator.appendImplEncoderForComposite(sb, level - 1, name, optionalFields);
75+
final RustGenerator.NullifyTargets nullifyTargets = RustGenerator.getNullifyTargets(fields);
76+
RustGenerator.appendImplEncoderForComposite(sb, level - 1, name, nullifyTargets);
7777

7878
// define impl...
7979
indent(sb, level - 1, "impl<'a, P> %s<P> where P: Encoder<'a> + Default {\n", name);

sbe-tool/src/test/resources/optional_enum_nullify.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,13 @@
2323
<validValue name="Alpha">3</validValue>
2424
<validValue name="Beta">4</validValue>
2525
</enum>
26+
<composite name="OptionalComposite">
27+
<type name="optionalCounter" primitiveType="uint16" presence="optional" nullValue="0"/>
28+
</composite>
2629
</types>
2730
<sbe:message name="optionalEnumNullify" id="1" description="optional enum nullify test">
2831
<field name="optionalEnum" type="EnumType" id="1" presence="optional"/>
2932
<field name="requiredEnumFromOptionalType" type="OptionalEncodingEnumType" id="2"/>
33+
<field name="optionalComposite" type="OptionalComposite" id="3"/>
3034
</sbe:message>
3135
</sbe:messageSchema>

0 commit comments

Comments
 (0)