@@ -198,6 +198,8 @@ private static (string, string)? GenerateForDataDefinition(
198198
199199 {{ GetWriter ( definition ) }}
200200
201+ {{ GetEquality ( definition ) }}
202+
201203 {{ GetValidator ( definition ) }}
202204
203205 {{ GetFieldDefinitions ( definition ) }}
@@ -1091,6 +1093,95 @@ public static void Write(
10911093 """ ;
10921094 }
10931095
1096+ private static string GetEquality ( DataDefinition definition )
1097+ {
1098+ var builder = new StringBuilder ( ) ;
1099+
1100+ if ( definition . GetFirstDataDefinitionBaseType ( ) is { } baseType )
1101+ {
1102+ builder . AppendLine ( $$ """
1103+ if (!{{ baseType . ToDisplayString ( ) }} .AreEqual(left, right, serialization, context))
1104+ return false;
1105+ """ ) ;
1106+ }
1107+
1108+ foreach ( var field in definition . Fields )
1109+ {
1110+ if ( ! definition . Type . Equals ( field . Symbol . ContainingType , SymbolEqualityComparer . Default ) )
1111+ continue ;
1112+
1113+ if ( field . Attribute . ReadOnly )
1114+ continue ;
1115+
1116+ var fieldType = field . Type . ToDisplayString ( ) ;
1117+ if ( IsMultidimensionalArray ( field . Type ) )
1118+ fieldType = fieldType . Replace ( "*" , "" ) ;
1119+
1120+ if ( field . Type . NullableAnnotation == NullableAnnotation . Annotated &&
1121+ ! fieldType . EndsWith ( "?" ) )
1122+ {
1123+ fieldType += "?" ;
1124+ }
1125+
1126+ var equalityExpression = GetFieldEqualityExpression ( field , fieldType ) ;
1127+
1128+ builder . AppendLine ( $$ """
1129+ if (!{{ equalityExpression }} )
1130+ return false;
1131+ """ ) ;
1132+ }
1133+
1134+ builder . AppendLine ( "return true;" ) ;
1135+
1136+ return $$ """
1137+ public static bool AreEqual(
1138+ {{ definition . GenericTypeName }} left,
1139+ {{ definition . GenericTypeName }} right,
1140+ ISerializationManager serialization,
1141+ ISerializationContext? context = null)
1142+ {
1143+ {{ builder }}
1144+ }
1145+ """ ;
1146+ }
1147+
1148+ private static string GetFieldEqualityExpression ( DataField field , string fieldType )
1149+ {
1150+ var fieldName = field . Symbol . Name ;
1151+
1152+ if ( TryGetFastHashSetEquality ( field . Type , $ "left.{ fieldName } ", $ "right.{ fieldName } ", out var hashSetEquality ) )
1153+ return hashSetEquality ;
1154+
1155+ if ( CanFieldUseDirectEquality ( field ) )
1156+ return $ "EqualityComparer<{ fieldType } >.Default.Equals(left.{ fieldName } , right.{ fieldName } )";
1157+
1158+ return $ "serialization.DataFieldEquals<{ fieldType } >(left.{ fieldName } , right.{ fieldName } , context)";
1159+ }
1160+
1161+ private static bool CanFieldUseDirectEquality ( DataField field )
1162+ {
1163+ return CanTypeBeCopiedByValue ( field . Type ) ;
1164+ }
1165+
1166+ private static bool TryGetFastHashSetEquality (
1167+ ITypeSymbol type ,
1168+ string leftAccess ,
1169+ string rightAccess ,
1170+ [ NotNullWhen ( true ) ] out string ? equality )
1171+ {
1172+ equality = null ;
1173+
1174+ if ( type . WithNullableAnnotation ( NullableAnnotation . None ) is not INamedTypeSymbol namedType ||
1175+ ! IsGenericCollectionType ( namedType , "HashSet" , 1 ) )
1176+ {
1177+ return false ;
1178+ }
1179+
1180+ equality =
1181+ $ "ReferenceEquals({ leftAccess } , { rightAccess } ) || ({ leftAccess } != null && { rightAccess } != null && { leftAccess } .SetEquals({ rightAccess } ))";
1182+ return true ;
1183+ }
1184+
10941185 private static string GetFieldDefinitions ( DataDefinition definition )
10951186 {
10961187 var builder = new StringBuilder ( ) ;
0 commit comments