@@ -7,6 +7,8 @@ use std::fmt;
77use std:: ops:: Range ;
88use std:: str;
99
10+ use serde:: Deserialize ;
11+
1012#[ cfg( feature = "uuid" ) ]
1113use uuid:: Uuid ;
1214
@@ -282,7 +284,7 @@ impl<'s> Iterator for ProguardRecordIter<'s> {
282284/// Maps start/end lines of a minified file to original start/end lines.
283285///
284286/// All line mappings are 1-based and inclusive.
285- #[ derive( Clone , Copy , Debug , PartialEq ) ]
287+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
286288pub struct LineMapping {
287289 /// Start Line, 1-based.
288290 pub startline : usize ,
@@ -294,8 +296,26 @@ pub struct LineMapping {
294296 pub original_endline : Option < usize > ,
295297}
296298
299+ /// An R8 header, as described in
300+ /// <https://r8.googlesource.com/r8/+/refs/heads/main/doc/retrace.md#additional-information-appended-as-comments-to-the-file>.
301+ ///
302+ /// The format is a line starting with `#` and followed by a JSON object.
303+ #[ derive( Debug , Clone , Deserialize , PartialEq , Eq ) ]
304+ #[ serde( tag = "id" , rename_all = "camelCase" ) ]
305+ pub enum R8Header < ' s > {
306+ /// A source file header, stating what source file a class originated from.
307+ ///
308+ /// See <https://r8.googlesource.com/r8/+/refs/heads/main/doc/retrace.md#source-file>.
309+ #[ serde( rename_all = "camelCase" ) ]
310+ SourceFile { file_name : & ' s str } ,
311+
312+ /// Catchall variant for headers we don't support.
313+ #[ serde( other) ]
314+ Other ,
315+ }
316+
297317/// A Proguard Mapping Record.
298- #[ derive( Clone , Debug , PartialEq ) ]
318+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
299319pub enum ProguardRecord < ' s > {
300320 /// A Proguard Header.
301321 Header {
@@ -304,6 +324,8 @@ pub enum ProguardRecord<'s> {
304324 /// Optional value if the Header is a KV pair.
305325 value : Option < & ' s str > ,
306326 } ,
327+ /// An R8 Header.
328+ R8Header ( R8Header < ' s > ) ,
307329 /// A Class Mapping.
308330 Class {
309331 /// Original name of the class.
@@ -436,7 +458,9 @@ impl<'s> ProguardRecord<'s> {
436458fn parse_proguard_record ( bytes : & [ u8 ] ) -> ( Result < ProguardRecord , ParseError > , & [ u8 ] ) {
437459 let bytes = consume_leading_newlines ( bytes) ;
438460
439- let result = if bytes. starts_with ( b"#" ) {
461+ let result = if bytes. starts_with ( b"# {" ) {
462+ parse_r8_header ( bytes)
463+ } else if bytes. starts_with ( b"#" ) {
440464 parse_proguard_header ( bytes)
441465 } else if bytes. starts_with ( b" " ) {
442466 parse_proguard_field_or_method ( bytes)
@@ -459,38 +483,35 @@ fn parse_proguard_record(bytes: &[u8]) -> (Result<ProguardRecord, ParseError>, &
459483 }
460484}
461485
462- const SOURCE_FILE_PREFIX : & [ u8 ; 32 ] = br#" {"id":"sourceFile","fileName":""# ;
463-
464486/// Parses a single Proguard Header from a Proguard File.
465487fn parse_proguard_header ( bytes : & [ u8 ] ) -> Result < ( ProguardRecord , & [ u8 ] ) , ParseError > {
466488 let bytes = parse_prefix ( bytes, b"#" ) ?;
467489
468- if let Ok ( bytes) = parse_prefix ( bytes, SOURCE_FILE_PREFIX ) {
469- let ( value, bytes) = parse_until ( bytes, |c| * c == b'"' ) ?;
470- let bytes = parse_prefix ( bytes, br#""}"# ) ?;
490+ // Existing logic for `key: value` format
491+ let ( key, bytes) = parse_until ( bytes, |c| * c == b':' || is_newline ( c) ) ?;
471492
472- let record = ProguardRecord :: Header {
473- key : "sourceFile" ,
474- value : Some ( value ) ,
475- } ;
493+ let ( value , bytes ) = match parse_prefix ( bytes , b":" ) {
494+ Ok ( bytes ) => parse_until ( bytes , is_newline ) . map ( | ( v , bytes ) | ( Some ( v ) , bytes ) ) ,
495+ Err ( _ ) => Ok ( ( None , bytes ) ) ,
496+ } ? ;
476497
477- Ok ( ( record , consume_leading_newlines ( bytes ) ) )
478- } else {
479- // Existing logic for `key : value` format
480- let ( key , bytes ) = parse_until ( bytes , |c| * c == b':' || is_newline ( c ) ) ? ;
498+ let record = ProguardRecord :: Header {
499+ key : key . trim ( ) ,
500+ value : value. map ( |v| v . trim ( ) ) ,
501+ } ;
481502
482- let ( value, bytes) = match parse_prefix ( bytes, b":" ) {
483- Ok ( bytes) => parse_until ( bytes, is_newline) . map ( |( v, bytes) | ( Some ( v) , bytes) ) ,
484- Err ( _) => Ok ( ( None , bytes) ) ,
485- } ?;
503+ Ok ( ( record, consume_leading_newlines ( bytes) ) )
504+ }
486505
487- let record = ProguardRecord :: Header {
488- key : key. trim ( ) ,
489- value : value. map ( |v| v. trim ( ) ) ,
490- } ;
506+ fn parse_r8_header ( bytes : & [ u8 ] ) -> Result < ( ProguardRecord , & [ u8 ] ) , ParseError > {
507+ let bytes = parse_prefix ( bytes, b"#" ) ?;
508+ let ( header, rest) = parse_until ( bytes, is_newline) ?;
491509
492- Ok ( ( record, consume_leading_newlines ( bytes) ) )
493- }
510+ let header = serde_json:: from_str ( header) . unwrap ( ) ;
511+ Ok ( (
512+ ProguardRecord :: R8Header ( header) ,
513+ consume_leading_newlines ( rest) ,
514+ ) )
494515}
495516
496517/// Parses a single Proguard Field or Method from a Proguard File.
@@ -763,10 +784,9 @@ mod tests {
763784 let parsed = ProguardRecord :: try_parse ( bytes) ;
764785 assert_eq ! (
765786 parsed,
766- Ok ( ProguardRecord :: Header {
767- key: "sourceFile" ,
768- value: Some ( "Foobar.kt" )
769- } )
787+ Ok ( ProguardRecord :: R8Header ( R8Header :: SourceFile {
788+ file_name: "Foobar.kt" ,
789+ } ) )
770790 ) ;
771791 }
772792
0 commit comments