From c87cc79ec994e59b9107df4166460bd1055705f7 Mon Sep 17 00:00:00 2001 From: mattsoup Date: Tue, 30 Jun 2026 15:00:54 -0400 Subject: [PATCH 1/3] adding parsing of the adapter_dimer field, for when it is present --- src/fastp.rs | 22 +++++++++++------- tests/fastp_test.rs | 40 ++++++++++++++++++++++++++++++++ tests/fixtures/fastp_chunk1.json | 1 + tests/fixtures/fastp_chunk2.json | 1 + 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/fastp.rs b/src/fastp.rs index d3dbcce..4db6cb6 100644 --- a/src/fastp.rs +++ b/src/fastp.rs @@ -178,17 +178,23 @@ fn merge_adapter_counts(dicts: &[&Map]) -> Result Result { - const FIELDS: &[&str] = &[ - "passed_filter_reads", - "low_quality_reads", - "too_many_N_reads", - "too_short_reads", - "too_long_reads", - ]; + let mut fields = vec!["passed_filter_reads", "low_quality_reads", "too_many_N_reads"]; + if results.iter().any(|r| r.get("adapter_dimer_reads").is_some()) { + fields.push("adapter_dimer_reads"); + } + fields.push("too_short_reads"); + fields.push("too_long_reads"); + let mut merged = Map::new(); - for field in FIELDS { + for field in &fields { let mut sum = 0_i64; for (i, result) in results.iter().enumerate() { sum += required_i64(result, field).with_context(|| format!("filtering_result[{i}]"))?; diff --git a/tests/fastp_test.rs b/tests/fastp_test.rs index 045b67f..41e9e54 100644 --- a/tests/fastp_test.rs +++ b/tests/fastp_test.rs @@ -191,8 +191,48 @@ fn test_merge_two_fixture_files_filtering_result() { assert_eq!(fr["passed_filter_reads"].as_i64(), Some(2850)); assert_eq!(fr["low_quality_reads"].as_i64(), Some(90)); assert_eq!(fr["too_many_N_reads"].as_i64(), Some(30)); + assert_eq!(fr["adapter_dimer_reads"].as_i64(), Some(408)); assert_eq!(fr["too_short_reads"].as_i64(), Some(30)); assert_eq!(fr["too_long_reads"].as_i64(), Some(0)); + + // adapter_dimer_reads must keep fastp's field order: between too_many_N_reads + // and too_short_reads. + let keys: Vec<&str> = fr.as_object().unwrap().keys().map(String::as_str).collect(); + assert_eq!( + keys, + [ + "passed_filter_reads", + "low_quality_reads", + "too_many_N_reads", + "adapter_dimer_reads", + "too_short_reads", + "too_long_reads", + ] + ); +} + +#[test] +fn test_merge_filtering_result_omits_adapter_dimer_when_absent() { + // Older fastp versions predate adapter_dimer_reads; merging must not invent it. + let chunk = json!({ + "summary": { + "before_filtering": {"total_reads": 10, "total_bases": 100, "q20_bases": 90, "q30_bases": 80}, + "after_filtering": {"total_reads": 10, "total_bases": 100, "q20_bases": 90, "q30_bases": 80} + }, + "filtering_result": { + "passed_filter_reads": 10, + "low_quality_reads": 0, + "too_many_N_reads": 0, + "too_short_reads": 0, + "too_long_reads": 0 + }, + "duplication": {"rate": 0.0}, + "adapter_cutting": {"adapter_trimmed_reads": 0, "adapter_trimmed_bases": 0} + }); + let merged = merge_jsons(&[chunk.clone(), chunk]).expect("merge should succeed"); + let fr = &merged["filtering_result"]; + assert!(fr.get("adapter_dimer_reads").is_none()); + assert_eq!(fr["passed_filter_reads"].as_i64(), Some(20)); } #[test] diff --git a/tests/fixtures/fastp_chunk1.json b/tests/fixtures/fastp_chunk1.json index af3b700..5872eb8 100644 --- a/tests/fixtures/fastp_chunk1.json +++ b/tests/fixtures/fastp_chunk1.json @@ -29,6 +29,7 @@ "passed_filter_reads": 950, "low_quality_reads": 30, "too_many_N_reads": 10, + "adapter_dimer_reads": 203, "too_short_reads": 10, "too_long_reads": 0 }, diff --git a/tests/fixtures/fastp_chunk2.json b/tests/fixtures/fastp_chunk2.json index 281a13b..5640e74 100644 --- a/tests/fixtures/fastp_chunk2.json +++ b/tests/fixtures/fastp_chunk2.json @@ -29,6 +29,7 @@ "passed_filter_reads": 1900, "low_quality_reads": 60, "too_many_N_reads": 20, + "adapter_dimer_reads": 205, "too_short_reads": 20, "too_long_reads": 0 }, From bb47a597bcb19de3bf69effbc784d532bb584501 Mon Sep 17 00:00:00 2001 From: mattsoup Date: Tue, 30 Jun 2026 15:34:12 -0400 Subject: [PATCH 2/3] Formatting --- src/fastp.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/fastp.rs b/src/fastp.rs index 4db6cb6..08f13f1 100644 --- a/src/fastp.rs +++ b/src/fastp.rs @@ -186,8 +186,15 @@ fn merge_adapter_counts(dicts: &[&Map]) -> Result Result { - let mut fields = vec!["passed_filter_reads", "low_quality_reads", "too_many_N_reads"]; - if results.iter().any(|r| r.get("adapter_dimer_reads").is_some()) { + let mut fields = vec![ + "passed_filter_reads", + "low_quality_reads", + "too_many_N_reads", + ]; + if results + .iter() + .any(|r| r.get("adapter_dimer_reads").is_some()) + { fields.push("adapter_dimer_reads"); } fields.push("too_short_reads"); From 141cfe42752595882cb9b0a835bffec3f8d5a580 Mon Sep 17 00:00:00 2001 From: mattsoup Date: Tue, 30 Jun 2026 15:42:26 -0400 Subject: [PATCH 3/3] Slightly cleaner --- src/fastp.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/fastp.rs b/src/fastp.rs index 08f13f1..c4f8072 100644 --- a/src/fastp.rs +++ b/src/fastp.rs @@ -178,30 +178,29 @@ fn merge_adapter_counts(dicts: &[&Map]) -> Result Result { - let mut fields = vec![ + const FIELDS: &[&str] = &[ "passed_filter_reads", "low_quality_reads", "too_many_N_reads", + "adapter_dimer_reads", + "too_short_reads", + "too_long_reads", ]; - if results + let has_dimer = results .iter() - .any(|r| r.get("adapter_dimer_reads").is_some()) - { - fields.push("adapter_dimer_reads"); - } - fields.push("too_short_reads"); - fields.push("too_long_reads"); + .any(|r| r.get("adapter_dimer_reads").is_some()); let mut merged = Map::new(); - for field in &fields { + for field in FIELDS { + if *field == "adapter_dimer_reads" && !has_dimer { + continue; + } let mut sum = 0_i64; for (i, result) in results.iter().enumerate() { sum += required_i64(result, field).with_context(|| format!("filtering_result[{i}]"))?;