WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Conversation

@msto
Copy link

@msto msto commented Dec 7, 2025

Closes #20.

This PR adds TemplateIterator, based on the similar constructs in Python and Scala. Like #26 , this was mostly Claude.

@msto msto self-assigned this Dec 7, 2025
Comment on lines +39 to +50
/// Errors that can occur when iterating over templates.
#[derive(Error, Debug)]
pub enum TemplateIteratorError {
/// Error building a template from records.
#[error("Failed to build template: {0}")]
TemplateBuildError(#[from] TemplateError),

/// Error reading a BAM record.
#[error("Failed to read BAM record: {0}")]
BamReadError(#[from] rust_htslib::errors::Error),
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nh13 @tfenne I am not sure as to your preference for Error specificity in Rust - is this okay, or would you prefer they be wrapped into FgError?

src/bam/mod.rs Outdated
Comment on lines 264 to 271
/// for template in TemplateIterator::new(reader.records()) {
/// let template = template?;
/// println!("Template: {:?}", template.name());
/// }
/// ```
pub struct TemplateIterator<I>
where
I: Iterator<Item = Result<Record, rust_htslib::errors::Error>>,
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't convinced that this should be an iterator over Result<Record, E> instead of an iterator over Record, because the return type of Reader.records() is Records, but claude pointed out that where Records implements Iterator, it's over Result<Record, E>.

https://docs.rs/rust-htslib/latest/rust_htslib/bam/struct.Records.html

https://github.com/rust-bio/rust-htslib/blob/8f1cdd75c301cb1e0ae54125a33de065c2550225/src/tbx/mod.rs#L328-L346

src/bam/mod.rs Outdated
//! fgbio (Scala) and fgpyo (Python).
use rust_htslib::bam::Record;
use std::iter::Peekable;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice that this is part of the std lib!

Comment on lines +350 to +366
/// Extension trait for creating a [`TemplateIterator`] from BAM record iterators.
///
/// This trait provides an ergonomic way to convert a BAM record iterator into
/// a template iterator using method chaining.
///
/// # Example
///
/// ```ignore
/// use fgoxide::bam::IntoTemplateIterator;
/// use rust_htslib::bam::Reader;
///
/// let reader = Reader::from_path("queryname_sorted.bam")?;
/// for template in reader.records().templates() {
/// let template = template?;
/// // process template...
/// }
/// ```
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super cute and I would not have discovered it on my own for a while. Thanks Claude!

}

mod template_iterator_tests {
use super::*;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd feel better if we had one test that read from an actual BAM file. @nh13 @tfenne I know the general preference is to synthesize test data on the fly, but would you be open to having one test case to read a small BAM? Maybe two read pairs? Otherwise we're not directly testing that TemplateIterator can wrap rust-htslib's Reader::from_path

@msto msto marked this pull request as ready for review December 7, 2025 00:50
@msto msto requested review from nh13 and tfenne as code owners December 7, 2025 00:50
@msto msto requested a review from theJasonFan December 7, 2025 00:50
@msto msto assigned nh13 and tfenne and unassigned msto Dec 7, 2025
msto and others added 4 commits December 7, 2025 15:37
FusedIterator is a marker trait that guarantees an iterator will always
return None after returning None the first time. Most iterators behave
this way naturally, but Rust doesn't assume it.

Benefits of implementing FusedIterator:
- Iterator::fuse() becomes a no-op, allowing the compiler to optimize
  away redundant fuse() calls
- Enables downstream optimizations for code consuming the iterator
- Documents the iterator's behavior to API users

TemplateIterator qualifies because once the inner iterator is exhausted,
there's no way to produce more templates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Move the specific Item type bound to impl blocks, keeping only the
minimal `I: Iterator` bound required by Peekable<I> on the struct
definition.

This follows the Rust convention of placing bounds on impl blocks
rather than struct definitions when possible, making the type more
flexible and improving compile-time error messages.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Replace verbose match/loop pattern with idiomatic `while let` for
collecting records with the same query name.

The `while let Some(Ok(rec)) = self.inner.peek()` pattern is cleaner
and handles the None and Err cases implicitly by exiting the loop,
which is the desired behavior (errors are deferred to the next
iteration).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
The #[must_use] attribute warns when the return value of new() is
discarded. Since TemplateIterator::new() creates an iterator that
must be consumed to have any effect, discarding it is almost certainly
a bug.

This follows the Rust API guidelines for constructors that return
values which should not be ignored.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants