@@ -37,10 +37,84 @@ use std::{collections::HashMap, fmt, mem, sync::OnceLock};
3737use memchr:: { memchr, memmem} ;
3838use serde:: { de, de:: IgnoredAny , Deserialize } ;
3939
40- use crate :: report:: pyreport:: { CHUNKS_FILE_END_OF_CHUNK , CHUNKS_FILE_HEADER_TERMINATOR } ;
40+ use super :: report_json:: ParsedReportJson ;
41+ use crate :: {
42+ error:: CodecovError ,
43+ report:: {
44+ models,
45+ pyreport:: {
46+ types:: { self , PyreportCoverage , ReportLine } ,
47+ CHUNKS_FILE_END_OF_CHUNK , CHUNKS_FILE_HEADER_TERMINATOR ,
48+ } ,
49+ Report , ReportBuilder ,
50+ } ,
51+ } ;
52+
53+ pub fn parse_chunks_file < B , R > (
54+ input : & [ u8 ] ,
55+ _report_json : & ParsedReportJson ,
56+ builder : & mut B ,
57+ ) -> Result < ( ) , CodecovError >
58+ where
59+ B : ReportBuilder < R > ,
60+ R : Report ,
61+ {
62+ let chunks_file = ChunksFile :: new ( input) ?;
63+
64+ let mut labels_index = HashMap :: with_capacity ( chunks_file. labels_index ( ) . len ( ) ) ;
65+ for ( index, name) in chunks_file. labels_index ( ) {
66+ let context = builder. insert_context ( name) ?;
67+ labels_index. insert ( index. clone ( ) , context. id ) ;
68+ }
69+
70+ let mut report_lines = vec ! [ ] ;
71+
72+ let mut chunks = chunks_file. chunks ( ) ;
73+ while let Some ( mut chunk) = chunks. next_chunk ( ) ? {
74+ let mut line_no = 0 ;
75+ report_lines. clear ( ) ;
76+ while let Some ( line) = chunk. next_line ( ) ? {
77+ line_no += 1 ;
78+ if let Some ( line) = line {
79+ let coverage_type = match line. 1 . unwrap_or_default ( ) {
80+ CoverageType :: Line => models:: CoverageType :: Line ,
81+ CoverageType :: Branch => models:: CoverageType :: Branch ,
82+ CoverageType :: Method => models:: CoverageType :: Method ,
83+ } ;
84+ let sessions = line
85+ . 2
86+ . into_iter ( )
87+ . map ( |session| types:: LineSession {
88+ session_id : session. 0 ,
89+ coverage : session. 1 . into ( ) ,
90+ branches : None , // TODO
91+ partials : None , // TODO
92+ complexity : None , // TODO
93+ } )
94+ . collect ( ) ;
95+
96+ let mut report_line = ReportLine {
97+ line_no,
98+ coverage : line. 0 . into ( ) ,
99+ coverage_type,
100+ sessions,
101+ _messages : None ,
102+ _complexity : None ,
103+ datapoints : None , // TODO
104+ } ;
105+ report_line. normalize ( ) ;
106+ report_lines. push ( report_line) ;
107+ }
108+ }
109+ // TODO:
110+ // utils::save_report_lines()?;
111+ }
112+
113+ Ok ( ( ) )
114+ }
41115
42116#[ derive( Debug , thiserror:: Error ) ]
43- pub enum ParserError {
117+ pub enum ChunksFileParseError {
44118 #[ error( "unexpected EOF" ) ]
45119 UnexpectedEof ,
46120 #[ error( "unexpected input" ) ]
@@ -53,12 +127,12 @@ pub enum ParserError {
53127 InvalidLineRecord ( #[ source] serde_json:: Error ) ,
54128}
55129
56- impl PartialEq for ParserError {
130+ impl PartialEq for ChunksFileParseError {
57131 fn eq ( & self , other : & Self ) -> bool {
58132 core:: mem:: discriminant ( self ) == core:: mem:: discriminant ( other)
59133 }
60134}
61- impl Eq for ParserError { }
135+ impl Eq for ChunksFileParseError { }
62136
63137#[ derive( Debug ) ]
64138pub struct ChunksFile < ' d > {
@@ -67,16 +141,16 @@ pub struct ChunksFile<'d> {
67141}
68142
69143impl < ' d > ChunksFile < ' d > {
70- pub fn new ( mut input : & ' d [ u8 ] ) -> Result < Self , ParserError > {
144+ pub fn new ( mut input : & ' d [ u8 ] ) -> Result < Self , ChunksFileParseError > {
71145 static HEADER_FINDER : OnceLock < memmem:: Finder > = OnceLock :: new ( ) ;
72146 let header_finder =
73147 HEADER_FINDER . get_or_init ( || memmem:: Finder :: new ( CHUNKS_FILE_HEADER_TERMINATOR ) ) ;
74148
75149 let file_header = if let Some ( pos) = header_finder. find ( input) {
76150 let header_bytes = & input[ ..pos] ;
77151 input = & input[ pos + header_finder. needle ( ) . len ( ) ..] ;
78- let file_header: FileHeader =
79- serde_json :: from_slice ( header_bytes ) . map_err ( ParserError :: InvalidFileHeader ) ?;
152+ let file_header: FileHeader = serde_json :: from_slice ( header_bytes )
153+ . map_err ( ChunksFileParseError :: InvalidFileHeader ) ?;
80154 file_header
81155 } else {
82156 FileHeader :: default ( )
@@ -99,7 +173,7 @@ pub struct Chunks<'d> {
99173}
100174
101175impl < ' d > Chunks < ' d > {
102- pub fn next_chunk ( & mut self ) -> Result < Option < Chunk < ' d > > , ParserError > {
176+ pub fn next_chunk ( & mut self ) -> Result < Option < Chunk < ' d > > , ChunksFileParseError > {
103177 if self . input . is_empty ( ) {
104178 return Ok ( None ) ;
105179 }
@@ -123,9 +197,10 @@ impl<'d> Chunks<'d> {
123197 } ) ) ;
124198 }
125199
126- let header_bytes = next_line ( & mut chunk_bytes) . ok_or ( ParserError :: UnexpectedInput ) ?;
127- let chunk_header: ChunkHeader =
128- serde_json:: from_slice ( header_bytes) . map_err ( ParserError :: InvalidFileHeader ) ?;
200+ let header_bytes =
201+ next_line ( & mut chunk_bytes) . ok_or ( ChunksFileParseError :: UnexpectedInput ) ?;
202+ let chunk_header: ChunkHeader = serde_json:: from_slice ( header_bytes)
203+ . map_err ( ChunksFileParseError :: InvalidFileHeader ) ?;
129204
130205 Ok ( Some ( Chunk {
131206 chunk_header,
@@ -144,7 +219,7 @@ impl<'d> Chunk<'d> {
144219 & self . chunk_header . present_sessions
145220 }
146221
147- pub fn next_line ( & mut self ) -> Result < Option < Option < LineRecord > > , ParserError > {
222+ pub fn next_line ( & mut self ) -> Result < Option < Option < LineRecord > > , ChunksFileParseError > {
148223 let Some ( line) = next_line ( & mut self . input ) else {
149224 return Ok ( None ) ;
150225 } ;
@@ -154,7 +229,7 @@ impl<'d> Chunk<'d> {
154229 }
155230
156231 let line_record: LineRecord =
157- serde_json:: from_slice ( line) . map_err ( ParserError :: InvalidLineRecord ) ?;
232+ serde_json:: from_slice ( line) . map_err ( ChunksFileParseError :: InvalidLineRecord ) ?;
158233 return Ok ( Some ( Some ( line_record) ) ) ;
159234 }
160235}
@@ -217,7 +292,7 @@ pub struct LineRecord(
217292#[ derive( Debug , Clone , PartialEq , Eq , Deserialize ) ]
218293pub struct LineSession (
219294 /// session id
220- u32 ,
295+ usize ,
221296 /// coverage
222297 Coverage ,
223298 /// TODO: branches
@@ -260,6 +335,18 @@ pub enum Coverage {
260335 HitCount ( u32 ) ,
261336}
262337
338+ impl Into < PyreportCoverage > for Coverage {
339+ fn into ( self ) -> PyreportCoverage {
340+ match self {
341+ Coverage :: Partial => PyreportCoverage :: Partial ( ) ,
342+ Coverage :: BranchTaken ( covered, total) => {
343+ PyreportCoverage :: BranchesTaken { covered, total }
344+ }
345+ Coverage :: HitCount ( hits) => PyreportCoverage :: HitCount ( hits) ,
346+ }
347+ }
348+ }
349+
263350impl < ' de > Deserialize < ' de > for Coverage {
264351 fn deserialize < D > ( deserializer : D ) -> Result < Coverage , D :: Error >
265352 where
0 commit comments