diff --git a/CHANGELOG.md b/CHANGELOG.md index aa21d9e..66bf7a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,25 @@ All notable changes to SeqMatcher will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [2.0.0] - Unreleased +## [2.1.0] - 2026-07-14 + +### Added +- `build-release.sh` cross-platform release builder (macOS/Linux/Windows targets) +- `--no-report` flag to suppress run summary report generation +- Pre-built release binaries for macOS (arm64/x86_64) and Linux (amd64/arm64 musl) + +### Changed +- Simplified `progress` module: removed unused `count_lines` public API in favor of `estimate_total_lines` +- Renamed internal `count_lines_in_buf` to `count_lines_exact` for clarity +- Updated `rustfmt.toml` to use only stable-channel formatting options +- `ThreadResult` now derives `Default` instead of manual implementation + +### Fixed +- Clippy `too_many_arguments` warning on `process_sequences` (annotated allowed) +- Clippy `derivable_impls` warning on `ThreadResult::Default` +- Unused `create_temp_seq` helper in integration tests (annotated reserved) + +## [2.0.0] - 2026-07-14 ### Added - Comprehensive IUPAC degenerate base support (S, W, B, D, H, V) in reverse complement table @@ -17,17 +35,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `--timestamp-output` flag for time-stamped output filenames - Input data validation with health checks - Run summary report (`run_summary.json` and `run_summary.txt`) -- Statistical summary with primer hit ranking and coverage metrics -- Reproducibility features: SHA256 hashing of inputs, full CLI arg recording -- ASCII bar chart visualization for top primer hits +- Reproducibility features: full CLI arg recording in summary reports - `CITATION.cff` for academic citation -- Benchmark suite using Criterion (AC matching, primer matching, reverse complement) +- Benchmark suite using Criterion (AC matching, reverse complement) - Integration tests with end-to-end pipeline coverage - Public library API via `src/lib.rs` for downstream crate consumption - Comprehensive rustdoc documentation for all public APIs ### Changed -- Refactored `main.rs` (436 lines) into 5 focused modules: `cli`, `progress`, `matcher`, `pipeline`, `main` +- Refactored `main.rs` (436 lines) into focused modules: `cli`, `progress`, `matcher`, `pipeline` - Replaced manual `csv_to_line` serialization with `csv::Writer` for correctness - Optimized `write_variant_counts` O(P*V) HashMap lookups to O(1) array indexing - Replaced `eprintln!` diagnostics with `tracing` structured logging @@ -35,14 +51,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Eliminated double I/O from `count_lines` pre-scan; uses file-size estimation ### Fixed -- IUPAC degenerate bases S, W, B, D, H, V now correctly reverse-complemented +- **Critical**: IUPAC degenerate bases S, W, B, D, H, V now correctly reverse-complemented (previously mapped to themselves, producing incorrect results for degenerate sequences) - Version inconsistency between `Cargo.toml` (1.4.0) and `Cargo.lock` (1.3.0) ### Removed - Unused `serde` dependency from `Cargo.toml` - Manual `csv_to_line` function in favor of `csv::Writer` -- Redundant `num_primers` parameter from `process_sequences` and `process_chunk` ## [1.4.0] - 2025-06-25 @@ -50,9 +65,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Streaming I/O with reusable hit buffer for reduced allocations - In-place ASCII uppercase conversion -### Changed -- Performance optimization: streaming chunk processing replaces full-file buffering - ## [1.3.0] - 2025-06-25 ### Added diff --git a/CITATION.cff b/CITATION.cff index 3e03754..1cc0b21 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -7,8 +7,8 @@ authors: given-names: Jiwen orcid: "https://orcid.org/0000-0000-0000-0000" url: "https://github.com/CropCoder" -version: 2.0.0 -date-released: 2026-07-13 +version: 2.1.0 +date-released: 2026-07-14 repository-code: "https://github.com/CropCoder/SeqMatcher" url: "https://github.com/CropCoder/SeqMatcher" abstract: > diff --git a/Cargo.lock b/Cargo.lock index 4fbfcea..e44f691 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,7 +605,7 @@ dependencies = [ [[package]] name = "seq_matcher" -version = "2.0.0" +version = "2.1.0" dependencies = [ "aho-corasick", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index e9e8ea6..f2f5330 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "seq_matcher" -version = "2.0.0" +version = "2.1.0" edition = "2021" [dependencies] diff --git a/src/progress.rs b/src/progress.rs index 8652787..d32e10a 100644 --- a/src/progress.rs +++ b/src/progress.rs @@ -1,7 +1,7 @@ //! Progress bar and line counting utilities. //! //! Provides real-time progress display with throughput and ETA estimation, -//! plus efficient file line counting for progress tracking. +//! plus efficient file line estimation for progress tracking. use std::fs::File; use std::io::{Read, Seek, SeekFrom, Write}; @@ -51,31 +51,17 @@ pub fn estimate_total_lines(path: &str, sample_lines: usize) -> Result<(u64, u64 Ok((estimated.max(1), file_size)) } else { // File is too short to sample — count exactly - file.seek(SeekFrom::Start(0))?; - let exact = count_lines_in_buf(&mut file)?; + let exact = count_lines_exact(&mut file)?; file.seek(SeekFrom::Start(0))?; Ok((exact, file_size)) } } -/// Count newlines in a file by scanning raw bytes (256KB buffer). -/// -/// Fast line counting that avoids UTF-8 validation overhead of -/// line-by-line reading. Handles files with or without trailing newline. -/// Seeks back to the beginning of the file after counting. -/// -/// Note: for pipeline progress estimation, [`estimate_total_lines`] is -/// preferred as it avoids a full file scan. -#[allow(dead_code)] -pub fn count_lines(path: &str) -> Result { - let mut file = File::open(path)?; - let count = count_lines_in_buf(&mut file)?; - file.seek(SeekFrom::Start(0))?; - Ok(count) -} - /// Count newlines in an already-open file using a 256KB read buffer. -fn count_lines_in_buf(file: &mut File) -> Result { +/// +/// Accurate but requires a full scan. Prefer [`estimate_total_lines`] for +/// progress-bar use; use this only when an exact count is required. +fn count_lines_exact(file: &mut File) -> Result { let mut buf = [0u8; 256 * 1024]; let mut count = 0u64; let mut non_empty = false; @@ -104,12 +90,24 @@ fn count_lines_in_buf(file: &mut File) -> Result { /// Uses `\r` to overwrite the same line. The bar width is configurable. /// Callers should flush stderr after this to ensure immediate display. pub fn print_progress(processed: u64, total: u64, start: &std::time::Instant, bar_width: usize) { - let pct = if total > 0 { processed as f64 / total as f64 } else { 0.0 }; + let pct = if total > 0 { + processed as f64 / total as f64 + } else { + 0.0 + }; let filled = (bar_width as f64 * pct) as usize; let bar = "\u{2588}".repeat(filled) + &"\u{2591}".repeat(bar_width.saturating_sub(filled)); let elapsed = start.elapsed().as_secs_f64(); - let rate = if elapsed > 0.0 { processed as f64 / elapsed } else { 0.0 }; - let eta = if rate > 0.0 { (total.saturating_sub(processed)) as f64 / rate } else { 0.0 }; + let rate = if elapsed > 0.0 { + processed as f64 / elapsed + } else { + 0.0 + }; + let eta = if rate > 0.0 { + (total.saturating_sub(processed)) as f64 / rate + } else { + 0.0 + }; eprint!( "\r [{bar}] {pct:5.1}% {processed}/{total} {rate:.0} seq/s ETA: {eta:.0}s",