1- use  crate :: data_contract:: serialized_version:: v0:: DataContractInSerializationFormatV0 ; 
2- use  crate :: data_contract:: { 
3-     DataContract ,  DefinitionName ,  DocumentName ,  GroupContractPosition ,  TokenContractPosition , 
4-     EMPTY_GROUPS ,  EMPTY_TOKENS , 
5- } ; 
6- use  crate :: version:: PlatformVersion ; 
7- use  std:: collections:: BTreeMap ; 
8- 
91use  super :: EMPTY_KEYWORDS ; 
102use  crate :: data_contract:: associated_token:: token_configuration:: TokenConfiguration ; 
113use  crate :: data_contract:: group:: Group ; 
4+ use  crate :: data_contract:: serialized_version:: v0:: DataContractInSerializationFormatV0 ; 
125use  crate :: data_contract:: serialized_version:: v1:: DataContractInSerializationFormatV1 ; 
136use  crate :: data_contract:: v0:: DataContractV0 ; 
147use  crate :: data_contract:: v1:: DataContractV1 ; 
8+ use  crate :: data_contract:: { 
9+     DataContract ,  DefinitionName ,  DocumentName ,  GroupContractPosition ,  TokenContractPosition , 
10+     EMPTY_GROUPS ,  EMPTY_TOKENS , 
11+ } ; 
1512use  crate :: validation:: operations:: ProtocolValidationOperation ; 
13+ use  crate :: version:: PlatformVersion ; 
1614use  crate :: ProtocolError ; 
1715use  bincode:: { Decode ,  Encode } ; 
1816use  derive_more:: From ; 
@@ -21,6 +19,8 @@ use platform_version::{IntoPlatformVersioned, TryFromPlatformVersioned};
2119use  platform_versioning:: PlatformVersioned ; 
2220#[ cfg( feature = "data-contract-serde-conversion" ) ]  
2321use  serde:: { Deserialize ,  Serialize } ; 
22+ use  std:: collections:: BTreeMap ; 
23+ use  std:: fmt; 
2424
2525pub ( in  crate :: data_contract)  mod  v0; 
2626pub ( in  crate :: data_contract)  mod  v1; 
@@ -34,6 +34,62 @@ pub mod property_names {
3434
3535pub  const  CONTRACT_DESERIALIZATION_LIMIT :  usize  = 15000 ; 
3636
37+ /// Represents a field mismatch between two `DataContractInSerializationFormat::V1` 
38+ /// variants, or indicates a format version mismatch. 
39+ /// 
40+ /// Used to diagnose why two data contracts are not considered equal 
41+ /// when ignoring auto-generated fields. 
42+ #[ derive( Debug ,  PartialEq ,  Eq ,  Clone ,  Copy ) ]  
43+ pub  enum  DataContractMismatch  { 
44+     /// The `id` fields are not equal. 
45+ Id , 
46+     /// The `config` fields are not equal. 
47+ Config , 
48+     /// The `version` fields are not equal. 
49+ Version , 
50+     /// The `owner_id` fields are not equal. 
51+ OwnerId , 
52+     /// The `schema_defs` fields are not equal. 
53+ SchemaDefs , 
54+     /// The `document_schemas` fields are not equal. 
55+ DocumentSchemas , 
56+     /// The `groups` fields are not equal. 
57+ Groups , 
58+     /// The `tokens` fields are not equal. 
59+ Tokens , 
60+     /// The `keywords` fields are not equal. 
61+ Keywords , 
62+     /// The `description` fields are not equal. 
63+ Description , 
64+     /// The two variants are of different serialization formats (e.g., V0 vs V1). 
65+ FormatVersionMismatch , 
66+     /// The two variants are different in V0. 
67+ V0Mismatch , 
68+ } 
69+ 
70+ impl  fmt:: Display  for  DataContractMismatch  { 
71+     /// Formats the enum into a human-readable string describing the mismatch. 
72+ fn  fmt ( & self ,  f :  & mut  fmt:: Formatter < ' _ > )  -> fmt:: Result  { 
73+         let  description = match  self  { 
74+             DataContractMismatch :: Id  => "ID fields differ" , 
75+             DataContractMismatch :: Config  => "Config fields differ" , 
76+             DataContractMismatch :: Version  => "Version fields differ" , 
77+             DataContractMismatch :: OwnerId  => "Owner ID fields differ" , 
78+             DataContractMismatch :: SchemaDefs  => "Schema definitions differ" , 
79+             DataContractMismatch :: DocumentSchemas  => "Document schemas differ" , 
80+             DataContractMismatch :: Groups  => "Groups differ" , 
81+             DataContractMismatch :: Tokens  => "Tokens differ" , 
82+             DataContractMismatch :: Keywords  => "Keywords differ" , 
83+             DataContractMismatch :: Description  => "Description fields differ" , 
84+             DataContractMismatch :: FormatVersionMismatch  => { 
85+                 "Serialization format versions differ (e.g., V0 vs V1)" 
86+             } 
87+             DataContractMismatch :: V0Mismatch  => "V0 versions differ" , 
88+         } ; 
89+         write ! ( f,  "{}" ,  description) 
90+     } 
91+ } 
92+ 
3793#[ derive( Debug ,  Clone ,  Encode ,  Decode ,  PartialEq ,  PlatformVersioned ,  From ) ]  
3894#[ cfg_attr(  
3995    feature = "data-contract-serde-conversion" ,  
@@ -112,36 +168,65 @@ impl DataContractInSerializationFormat {
112168        } 
113169    } 
114170
115-     pub  fn  eq_without_auto_fields ( & self ,  other :  & Self )  -> bool  { 
171+     /// Compares `self` to another `DataContractInSerializationFormat` instance 
172+ /// and returns the first mismatching field, if any. 
173+ /// 
174+ /// This comparison ignores auto-generated fields and is only sensitive to 
175+ /// significant differences in contract content. For V0 formats, any difference 
176+ /// results in a generic mismatch. For differing format versions (V0 vs V1), 
177+ /// a `FormatVersionMismatch` is returned. 
178+ /// 
179+ /// # Returns 
180+ /// 
181+ /// - `None` if the contracts are equal according to the relevant fields. 
182+ /// - `Some(DataContractMismatch)` indicating the first field where they differ. 
183+ pub  fn  first_mismatch ( & self ,  other :  & Self )  -> Option < DataContractMismatch >  { 
116184        match  ( self ,  other)  { 
117185            ( 
118186                DataContractInSerializationFormat :: V0 ( v0_self) , 
119187                DataContractInSerializationFormat :: V0 ( v0_other) , 
120-             )  => v0_self == v0_other, 
188+             )  => { 
189+                 if  v0_self != v0_other { 
190+                     Some ( DataContractMismatch :: V0Mismatch ) 
191+                 }  else  { 
192+                     None 
193+                 } 
194+             } 
121195            ( 
122196                DataContractInSerializationFormat :: V1 ( v1_self) , 
123197                DataContractInSerializationFormat :: V1 ( v1_other) , 
124198            )  => { 
125-                 v1_self. id  == v1_other. id 
126-                     && v1_self. config  == v1_other. config 
127-                     && v1_self. version  == v1_other. version 
128-                     && v1_self. owner_id  == v1_other. owner_id 
129-                     && v1_self. schema_defs  == v1_other. schema_defs 
130-                     && v1_self. document_schemas  == v1_other. document_schemas 
131-                     && v1_self. groups  == v1_other. groups 
132-                     && v1_self. tokens  == v1_other. tokens 
133-                     && v1_self. keywords  == v1_other. keywords 
134-                     && v1_self. description  == v1_other. description 
199+                 if  v1_self. id  != v1_other. id  { 
200+                     Some ( DataContractMismatch :: Id ) 
201+                 }  else  if  v1_self. config  != v1_other. config  { 
202+                     Some ( DataContractMismatch :: Config ) 
203+                 }  else  if  v1_self. version  != v1_other. version  { 
204+                     Some ( DataContractMismatch :: Version ) 
205+                 }  else  if  v1_self. owner_id  != v1_other. owner_id  { 
206+                     Some ( DataContractMismatch :: OwnerId ) 
207+                 }  else  if  v1_self. schema_defs  != v1_other. schema_defs  { 
208+                     Some ( DataContractMismatch :: SchemaDefs ) 
209+                 }  else  if  v1_self. document_schemas  != v1_other. document_schemas  { 
210+                     Some ( DataContractMismatch :: DocumentSchemas ) 
211+                 }  else  if  v1_self. groups  != v1_other. groups  { 
212+                     Some ( DataContractMismatch :: Groups ) 
213+                 }  else  if  v1_self. tokens  != v1_other. tokens  { 
214+                     Some ( DataContractMismatch :: Tokens ) 
215+                 }  else  if  v1_self. keywords . len ( )  != v1_other. keywords . len ( ) 
216+                     || v1_self
217+                         . keywords 
218+                         . iter ( ) 
219+                         . zip ( v1_other. keywords . iter ( ) ) 
220+                         . any ( |( a,  b) | a. to_lowercase ( )  != b. to_lowercase ( ) ) 
221+                 { 
222+                     Some ( DataContractMismatch :: Keywords ) 
223+                 }  else  if  v1_self. description  != v1_other. description  { 
224+                     Some ( DataContractMismatch :: Description ) 
225+                 }  else  { 
226+                     None 
227+                 } 
135228            } 
136-             // Cross-version comparisons return false 
137-             ( 
138-                 DataContractInSerializationFormat :: V0 ( _) , 
139-                 DataContractInSerializationFormat :: V1 ( _) , 
140-             ) 
141-             | ( 
142-                 DataContractInSerializationFormat :: V1 ( _) , 
143-                 DataContractInSerializationFormat :: V0 ( _) , 
144-             )  => false , 
229+             _ => Some ( DataContractMismatch :: FormatVersionMismatch ) , 
145230        } 
146231    } 
147232} 
0 commit comments