Skip to content

Commit

Permalink
Parse report_json files with serde (#18)
Browse files Browse the repository at this point in the history
Replaces the hand-written `winnow`-based parser with a bunch of struct definitions along with deriving `serde::Deserialize`.
  • Loading branch information
Swatinem authored Aug 30, 2024
1 parent 1b43a89 commit 5f3091a
Show file tree
Hide file tree
Showing 9 changed files with 454 additions and 1,025 deletions.
54 changes: 32 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ members = ["bindings", "core"]
default-members = ["core"]

[profile.release]

debug = 1
3 changes: 3 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ edition = "2021"
[features]
default = ["pyreport"]
pyreport = []
testing = []

[dependencies]
include_dir = "0.7.3"
Expand All @@ -14,6 +15,7 @@ rand = "0.8.5"
rusqlite = { version = "0.31.0", features = ["bundled", "limits"] }
rusqlite_migration = { version = "1.2.0", features = ["from-directory"] }
seahash = "4.1.0"
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.117"
thiserror = "1.0.59"
winnow = "0.5.34"
Expand All @@ -25,3 +27,4 @@ tempfile = "3.9.0"
[[bench]]
name = "pyreport"
harness = false
required-features = ["testing"]
179 changes: 12 additions & 167 deletions core/benches/pyreport.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
use codecov_rs::{
error::Result,
parsers::{common::ReportBuilderCtx, pyreport::report_json},
report::{models, Report, ReportBuilder},
};
use codecov_rs::{parsers::pyreport::report_json, report::test::TestReportBuilder};
use divan::Bencher;
use winnow::Parser as _;

fn main() {
divan::main();
Expand All @@ -13,11 +8,11 @@ fn main() {
#[divan::bench]
fn simple_report() {
let reports = &[
r#"{"files": {"src/report.rs": [0, {}, [], null]}, "sessions": {"0": {"j": "codecov-rs CI"}}}"#,
r#"{"files": {"src/report.rs": [0, {}, [], null], "src/report/models.rs": [1, {}, [], null]}, "sessions": {"0": {"j": "codecov-rs CI"}, "1": {"j": "codecov-rs CI 2"}}}"#,
r#"{"files": {}, "sessions": {"0": {"j": "codecov-rs CI"}, "1": {"j": "codecov-rs CI 2"}}}"#,
r#"{"files": {"src/report.rs": [0, {}, [], null], "src/report/models.rs": [1, {}, [], null]}, "sessions": {}}"#,
r#"{"files": {}, "sessions": {}}"#,
&br#"{"files": {"src/report.rs": [0, {}, [], null]}, "sessions": {"0": {"j": "codecov-rs CI"}}}"#[..],
&br#"{"files": {"src/report.rs": [0, {}, [], null], "src/report/models.rs": [1, {}, [], null]}, "sessions": {"0": {"j": "codecov-rs CI"}, "1": {"j": "codecov-rs CI 2"}}}"#[..],
&br#"{"files": {}, "sessions": {"0": {"j": "codecov-rs CI"}, "1": {"j": "codecov-rs CI 2"}}}"#[..],
&br#"{"files": {"src/report.rs": [0, {}, [], null], "src/report/models.rs": [1, {}, [], null]}, "sessions": {}}"#[..],
&br#"{"files": {}, "sessions": {}}"#[..],
];

for input in reports {
Expand All @@ -26,175 +21,25 @@ fn simple_report() {
}

// parsing this is quite slow
#[divan::bench(sample_count = 10)]
#[divan::bench]
fn complex_report(bencher: Bencher) {
// this is a ~11M `report_json`
let path =
"./fixtures/pyreport/large/worker-c71ddfd4cb1753c7a540e5248c2beaa079fc3341-report_json.json";
let Ok(report) = std::fs::read_to_string(path) else {
let Ok(report) = std::fs::read(path) else {
println!("Failed to read test report");
return;
};

if report.starts_with("version https://git-lfs.github.com/spec/v1\n") {
if report.starts_with(b"version https://git-lfs.github.com/spec/v1\n") {
println!("Sample report has not been pulled from Git LFS");
return;
}

bencher.bench(|| run_parsing(&report));
}

fn run_parsing(input: &str) {
let report_builder = TestReport::default();
let mut stream = report_json::ReportOutputStream::<&str, TestReport, TestReport> {
input,
state: ReportBuilderCtx::new(report_builder),
};
report_json::parse_report_json
.parse_next(&mut stream)
.unwrap();
}

#[derive(Debug, Default)]
struct TestReport {
files: Vec<models::SourceFile>,
uploads: Vec<models::RawUpload>,
}

impl Report for TestReport {
fn list_files(&self) -> Result<Vec<models::SourceFile>> {
todo!()
}

fn list_contexts(&self) -> Result<Vec<models::Context>> {
todo!()
}

fn list_coverage_samples(&self) -> Result<Vec<models::CoverageSample>> {
todo!()
}

fn list_branches_for_sample(
&self,
_sample: &models::CoverageSample,
) -> Result<Vec<models::BranchesData>> {
todo!()
}

fn get_method_for_sample(
&self,
_sample: &models::CoverageSample,
) -> Result<Option<models::MethodData>> {
todo!()
}

fn list_spans_for_sample(
&self,
_sample: &models::CoverageSample,
) -> Result<Vec<models::SpanData>> {
todo!()
}

fn list_contexts_for_sample(
&self,
_sample: &models::CoverageSample,
) -> Result<Vec<models::Context>> {
todo!()
}

fn list_samples_for_file(
&self,
_file: &models::SourceFile,
) -> Result<Vec<models::CoverageSample>> {
todo!()
}

fn list_raw_uploads(&self) -> Result<Vec<models::RawUpload>> {
todo!()
}

fn merge(&mut self, _other: &Self) -> Result<()> {
todo!()
}

fn totals(&self) -> Result<models::ReportTotals> {
todo!()
}
}

impl ReportBuilder<TestReport> for TestReport {
fn insert_file(&mut self, path: &str) -> Result<models::SourceFile> {
let file = models::SourceFile::new(path);
self.files.push(file.clone());
Ok(file)
}

fn insert_raw_upload(
&mut self,
mut upload_details: models::RawUpload,
) -> Result<models::RawUpload> {
upload_details.id = self.uploads.len() as i64;
self.uploads.push(upload_details.clone());
Ok(upload_details)
}

fn insert_context(&mut self, _name: &str) -> Result<models::Context> {
todo!()
}

fn insert_coverage_sample(
&mut self,
_sample: models::CoverageSample,
) -> Result<models::CoverageSample> {
todo!()
}

fn multi_insert_coverage_sample(
&mut self,
_samples: Vec<&mut models::CoverageSample>,
) -> Result<()> {
todo!()
}

fn insert_branches_data(
&mut self,
_branch: models::BranchesData,
) -> Result<models::BranchesData> {
todo!()
}

fn multi_insert_branches_data(
&mut self,
_branches: Vec<&mut models::BranchesData>,
) -> Result<()> {
todo!()
}

fn insert_method_data(&mut self, _method: models::MethodData) -> Result<models::MethodData> {
todo!()
}

fn multi_insert_method_data(&mut self, _methods: Vec<&mut models::MethodData>) -> Result<()> {
todo!()
}

fn insert_span_data(&mut self, _span: models::SpanData) -> Result<models::SpanData> {
todo!()
}

fn multi_insert_span_data(&mut self, _spans: Vec<&mut models::SpanData>) -> Result<()> {
todo!()
}

fn associate_context(&mut self, _assoc: models::ContextAssoc) -> Result<models::ContextAssoc> {
todo!()
}

fn multi_associate_context(&mut self, _assocs: Vec<&mut models::ContextAssoc>) -> Result<()> {
todo!()
}

fn build(self) -> Result<Self> {
Ok(self)
}
fn run_parsing(input: &[u8]) {
let mut report_builder = TestReportBuilder::default();
report_json::parse_report_json(input, &mut report_builder).unwrap();
}
Loading

0 comments on commit 5f3091a

Please sign in to comment.