11use debugid:: DebugId ;
22use serde:: de:: IgnoredAny ;
3- use serde:: { Deserialize , Serialize } ;
3+ use serde:: { Deserialize , Deserializer , Serialize } ;
44use serde_json:: Value ;
5+ use std:: fmt:: Debug ;
56
67#[ derive( Serialize , Deserialize , PartialEq , Debug ) ]
78pub struct RawSectionOffset {
@@ -54,12 +55,8 @@ pub struct RawSourceMap {
5455 pub x_metro_module_paths : Option < Vec < String > > ,
5556 #[ serde( skip_serializing_if = "Option::is_none" ) ]
5657 pub x_facebook_sources : FacebookSources ,
57- #[ serde( skip_serializing_if = "Option::is_none" ) ]
58- pub debug_id : Option < DebugId > ,
59- // This field only exists to be able to deserialize from "debugId" keys
60- // if "debug_id" is unset.
61- #[ serde( skip_serializing_if = "Option::is_none" , rename = "debugId" ) ]
62- pub ( crate ) _debug_id_new : Option < DebugId > ,
58+ #[ serde( flatten) ]
59+ pub debug_id : DebugIdField ,
6360}
6461
6562#[ derive( Deserialize ) ]
@@ -75,3 +72,91 @@ pub struct MinimalRawSourceMap {
7572 pub names : Option < IgnoredAny > ,
7673 pub mappings : Option < IgnoredAny > ,
7774}
75+
76+ /// This struct represents a `RawSourceMap`'s debug ID fields.
77+ ///
78+ /// The reason this exists as a seperate struct is so that we can have custom deserialization
79+ /// logic, which can read both the legacy snake_case debug_id and the new camelCase debugId
80+ /// fields. In case both are provided, the camelCase field takes precedence.
81+ ///
82+ /// The field is always serialized as `debugId`.
83+ #[ derive( Serialize , Clone , PartialEq , Debug , Default ) ]
84+ pub ( crate ) struct DebugIdField {
85+ #[ serde( rename = "debugId" , skip_serializing_if = "Option::is_none" ) ]
86+ value : Option < DebugId > ,
87+ }
88+
89+ impl < ' de > Deserialize < ' de > for DebugIdField {
90+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
91+ where
92+ D : Deserializer < ' de > ,
93+ {
94+ // We cannot use serde(alias), as that would cause an error when both fields are present.
95+
96+ #[ derive( Deserialize ) ]
97+ struct Helper {
98+ #[ serde( rename = "debugId" ) ]
99+ camel : Option < DebugId > ,
100+ #[ serde( rename = "debug_id" ) ]
101+ legacy : Option < DebugId > ,
102+ }
103+
104+ let Helper { camel, legacy } = Helper :: deserialize ( deserializer) ?;
105+ Ok ( camel. or ( legacy) . into ( ) )
106+ }
107+ }
108+
109+ impl From < Option < DebugId > > for DebugIdField {
110+ fn from ( value : Option < DebugId > ) -> Self {
111+ Self { value }
112+ }
113+ }
114+
115+ impl From < DebugIdField > for Option < DebugId > {
116+ fn from ( value : DebugIdField ) -> Self {
117+ value. value
118+ }
119+ }
120+
121+ #[ cfg( test) ]
122+ mod tests {
123+ use super :: * ;
124+ use serde_json:: json;
125+
126+ fn parse_debug_id ( input : & str ) -> DebugId {
127+ input. parse ( ) . expect ( "valid debug id" )
128+ }
129+
130+ fn empty_sourcemap ( ) -> RawSourceMap {
131+ serde_json:: from_value :: < RawSourceMap > ( serde_json:: json!( { } ) )
132+ . expect ( "can deserialize empty JSON to RawSourceMap" )
133+ }
134+
135+ #[ test]
136+ fn raw_sourcemap_serializes_camel_case_debug_id ( ) {
137+ let camel = "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" ;
138+ let raw = RawSourceMap {
139+ debug_id : Some ( parse_debug_id ( camel) ) . into ( ) ,
140+ ..empty_sourcemap ( )
141+ } ;
142+
143+ let value = serde_json:: to_value ( raw) . expect ( "should serialize without error" ) ;
144+ let obj = value. as_object ( ) . expect ( "should be an object" ) ;
145+ assert ! ( obj. get( "debug_id" ) . is_none( ) ) ;
146+ assert_eq ! ( obj. get( "debugId" ) , Some ( & json!( parse_debug_id( camel) ) ) ) ;
147+ }
148+
149+ #[ test]
150+ fn raw_sourcemap_prefers_camel_case_on_deserialize ( ) {
151+ let legacy = "ffffffffffffffffffffffffffffffff" ;
152+ let camel = "00000000000000000000000000000000" ;
153+ let json = serde_json:: json!( {
154+ "debug_id" : legacy,
155+ "debugId" : camel
156+ } ) ;
157+ let raw: RawSourceMap =
158+ serde_json:: from_value ( json) . expect ( "can deserialize as RawSourceMap" ) ;
159+ let value: Option < DebugId > = raw. debug_id . into ( ) ;
160+ assert_eq ! ( value, Some ( parse_debug_id( camel) ) ) ;
161+ }
162+ }
0 commit comments