zefchain / serde-reflection

Rust libraries and tools to help with interoperability and testing of serialization formats based on Serde.
Apache License 2.0
137 stars 26 forks source link

add copyWith utility method to Dart classes #17

Closed kturney closed 2 years ago

kturney commented 2 years ago

Summary

Add the common copyWith utility method to generated Dart classes.

Uses this method for allowing nullable properties to be set to null.

Additionally, I noticed some incorrect formatting of the operator == method output while inspecting the code. I added a fix for that, but I can split into a separate PR if that is preferable.

Example other_types.dart output ```dart part of example_types; @immutable class OtherTypes { const OtherTypes({ required this.fString, required this.fBytes, this.fOption, required this.fUnit, required this.fSeq, required this.fTuple, required this.fStringmap, required this.fIntset, required this.fNestedSeq, }); OtherTypes.deserialize(BinaryDeserializer deserializer) : fString = deserializer.deserializeString(), fBytes = deserializer.deserializeBytes(), fOption = TraitHelpers.deserializeOptionStruct(deserializer), fUnit = deserializer.deserializeUnit(), fSeq = TraitHelpers.deserializeVectorStruct(deserializer), fTuple = TraitHelpers.deserializeTuple2U8U16(deserializer), fStringmap = TraitHelpers.deserializeMapStrToU32(deserializer), fIntset = TraitHelpers.deserializeMapU64ToUnit(deserializer), fNestedSeq = TraitHelpers.deserializeVectorVectorStruct(deserializer); static OtherTypes bincodeDeserialize(Uint8List input) { final deserializer = BincodeDeserializer(input); final value = OtherTypes.deserialize(deserializer); if (deserializer.offset < input.length) { throw Exception('Some input bytes were not read'); } return value; } static OtherTypes bcsDeserialize(Uint8List input) { final deserializer = BcsDeserializer(input); final value = OtherTypes.deserialize(deserializer); if (deserializer.offset < input.length) { throw Exception('Some input bytes were not read'); } return value; } final String fString; final Bytes fBytes; final Struct? fOption; final Unit fUnit; final List fSeq; final Tuple2 fTuple; final Map fStringmap; final Map fIntset; final List> fNestedSeq; OtherTypes copyWith({ String? fString, Bytes? fBytes, Struct? Function()? fOption, Unit? fUnit, List? fSeq, Tuple2? fTuple, Map? fStringmap, Map? fIntset, List>? fNestedSeq, }) { return OtherTypes( fString: fString ?? this.fString, fBytes: fBytes ?? this.fBytes, fOption: fOption == null ? this.fOption : fOption(), fUnit: fUnit ?? this.fUnit, fSeq: fSeq ?? this.fSeq, fTuple: fTuple ?? this.fTuple, fStringmap: fStringmap ?? this.fStringmap, fIntset: fIntset ?? this.fIntset, fNestedSeq: fNestedSeq ?? this.fNestedSeq, ); } void serialize(BinarySerializer serializer) { serializer.serializeString(fString); serializer.serializeBytes(fBytes); TraitHelpers.serializeOptionStruct(fOption, serializer); serializer.serializeUnit(fUnit); TraitHelpers.serializeVectorStruct(fSeq, serializer); TraitHelpers.serializeTuple2U8U16(fTuple, serializer); TraitHelpers.serializeMapStrToU32(fStringmap, serializer); TraitHelpers.serializeMapU64ToUnit(fIntset, serializer); TraitHelpers.serializeVectorVectorStruct(fNestedSeq, serializer); } Uint8List bincodeSerialize() { final serializer = BincodeSerializer(); serialize(serializer); return serializer.bytes; } Uint8List bcsSerialize() { final serializer = BcsSerializer(); serialize(serializer); return serializer.bytes; } @override bool operator ==(Object other) { if (identical(this, other)) return true; if (other.runtimeType != runtimeType) return false; return other is OtherTypes && fString == other.fString && fBytes == other.fBytes && fOption == other.fOption && fUnit == other.fUnit && listEquals(fSeq, other.fSeq) && fTuple == other.fTuple && mapEquals(fStringmap, other.fStringmap) && mapEquals(fIntset, other.fIntset) && listEquals(fNestedSeq, other.fNestedSeq); } @override int get hashCode => Object.hash( fString, fBytes, fOption, fUnit, fSeq, fTuple, fStringmap, fIntset, fNestedSeq, ); @override String toString() { String? fullString; assert(() { fullString = '$runtimeType(' 'fString: $fString, ' 'fBytes: $fBytes, ' 'fOption: $fOption, ' 'fUnit: $fUnit, ' 'fSeq: $fSeq, ' 'fTuple: $fTuple, ' 'fStringmap: $fStringmap, ' 'fIntset: $fIntset, ' 'fNestedSeq: $fNestedSeq' ')'; return true; }()); return fullString ?? 'OtherTypes'; } } ```

Test Plan

I'm not sure if this needs separate tests outside of analyze still passing. To verify my code worked, I logged out the path for one of the test projects and checked the output. I'm not sure if there is a better way to inspect generated code.

cc: @jerel

jerel commented 2 years ago

Yep, I'm happy with this approach. We've been using this style of copyWith for a while in our project.