Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump to v0.3.2 #16

Merged
merged 10 commits into from
Aug 9, 2023
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
# version 0.3.2 (2023-08-10)
## Bugfixes
- ***Temporary*** workaround to issue #8 : Providing `pmd-mask` with an invalid or corrupted fasta index now leads to an uncoverable error. This workaround.
- Remove debug and memory profiling artifacts (e.g. `#[inline(never)]`) on some performance critical functions.

## Documentation
- Complete [genome::Strand](src/genome/strand/mod.rs) documentation + doctests.
- Complete [genome::Orientation](src/genome/orientation/mod.rs) documentation + doctests.
- Complete [genome::coordinate::Position](src/genome/coordinate/position.rs) and [genome::coordinate::ChrName](src/genome/coordinate/chr_name/mod.rs) documentation + doctests.
- Improved [logger::Logger](src/logger/mod.rs) documentation
- Complete documentation and doctests for [genome::mask::MaskEntry](src/genome/mask/entry/mod.rs), [genome::mask::MaskThreshold](src/genome/mask/threshold/mod.rs) and [genome::mask::Masks](src/mask/mod.rs).
- Improved [genome::misincorporation::Misincorporations](src/misincorporation/mod.rs) and [genome::misincorporation::MisincorporationRecord](src/misincorporation/record/mod.rs) documentation + doctests.
- Improved [parser::Cli](src/parser/mod.rs) and [`pmd_mask`](src/lib.rs) higher-level functions documentation.

# version 0.3.1 (2023-07-19)
## Bugfixes
- Fixes issue #13 : `mask_sequence()` function now correctly handles out of index errors when attempting to retrieve positions that lie beyond the chromosome's length. A `ReferenceOutOfIndexError` is now raised to the `mask_5p()` and `mask_3p()` functions, while `apply_pmd_mask()` now recover from such an error if and only if the read happens to be labeled as 'segment unmapped' (`0x4`).
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "pmd-mask"
version = "0.3.1"
version = "0.3.2"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
3 changes: 3 additions & 0 deletions Cross.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build]
default-target = "x86_64-unknown-linux-gnu"
pre-build = ["apt-get update && apt-get --assume-yes install zlib1g-dev libbz2-dev liblzma-dev clang pkg-config"]
9 changes: 8 additions & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use thiserror::Error;

/// Main runtime error type enum for [`pmd-mask`](`crate`)
#[derive(Debug, Error)]
pub enum RuntimeError {
#[error(transparent)]
Expand Down Expand Up @@ -27,7 +28,13 @@ pub enum RuntimeError {
"Neither '--bam', nor the standard input is being sollicited at this point. \
Please provide pmd-mask with an input to work from, either through piping, or with the --bam argument"
)]
// #[error("Error")]
NoStdin,

#[error(
"Failed to load fasta index into memory. \
Ensure the provided fasta isn't corrupted, and is properly indexed with a companion '.fai' \
file within the same directory."
)]
LoadFaidx,
}

1 change: 1 addition & 0 deletions src/genome/coordinate/chr_name/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use thiserror::Error;

/// Error states for [`crate::genome::ChrName`]
#[derive(Debug, Error, PartialEq)]
pub enum ChrNameError {
#[error("Failed to convert Htslib record into a valid ChrName")]
Expand Down
10 changes: 10 additions & 0 deletions src/genome/coordinate/chr_name/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ impl ChrName {
}

impl Display for ChrName {
/// Return a formatted [`String`] representation of a [`ChrName`]
///
/// # Example
/// ```
/// use pmd_mask::genome::ChrName;
///
/// let chr = ChrName::new("MT");
/// assert_eq!(format!("{chr: ^8}"), " MT ");
/// ```
///
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
Expand Down
46 changes: 45 additions & 1 deletion src/genome/coordinate/position.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,70 @@
use std::{fmt::{self, Display, Formatter}};
use serde::Deserialize;

/// Position within a chromosome or read in base pair
/// Absolute or relative position within a chromosome or read (in base pairs).
///
/// This is just a struct containing a [`usize`].
#[derive(Debug, Clone, Copy, Deserialize, Hash, PartialEq, Eq)]
pub struct Position(usize);

impl Display for Position {
/// Return a formatted [`String`] representation of a [`Position`].
///
/// # Example
/// ```
/// use pmd_mask::genome::Position;
/// let position: Position = 42.into();
/// assert_eq!(&format!("{position: ^6}"), " 42 ");
/// ```
///
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl Position {
/// Create a new [`Position`] from a usize
///
/// # Example
/// ```
/// use pmd_mask::genome::Position;
///
/// let position = Position::new(42usize);
/// assert_eq!(position, 42.into());
/// ```
///
pub fn new(value: usize) -> Self {
Self(value)
}

//// Get the inner `usize` contained within a [`Position`] back
///
/// # Example
/// ```
/// use pmd_mask::genome::Position;
///
/// assert_eq!(Position::from(42).inner(), 42);
/// ```
///
pub fn inner(&self) -> usize {
self.0
}
}

impl From<usize> for Position {
/// Convert a [`usize`] from and into a [`Position`]
/// ```
/// use pmd_mask::genome::Position;
///
/// let pos1: Position = 55.into();
/// let pos2: Position = Position::from(55);
/// assert_eq!(pos1, pos2);
/// ```
fn from(value: usize) -> Self {
Position(value)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
22 changes: 22 additions & 0 deletions src/genome/orientation/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
use std::{fmt::{self, Display, Formatter}};
use serde::Deserialize;

/// Encodes the orientation of a relative bam record position. Two possible variants:
/// - [`Orientation::ThreePrime`]|`'3p'`: 3'OH end of a fragment
/// - [`Orientation::FivePrime`]|`'5p'`: 5'P end of a fragment
///
/// Largely associated with [`crate::misincorporation::MisincorporationRecord`],
/// to encode the relative position of a nucleotide within a read.
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, Hash)]
pub enum Orientation {
#[serde(rename = "3p")]
Expand All @@ -10,6 +16,14 @@ pub enum Orientation {
}

impl AsRef<str> for Orientation {
/// Obtain the [`str`] representation of an [`Orientation`]
/// ```
/// use pmd_mask::genome::Orientation;
///
/// assert_eq!(Orientation::ThreePrime.as_ref(), "3p");
/// assert_eq!(Orientation::FivePrime.as_ref(), "5p");
///
/// ```
fn as_ref(&self) -> &str {
match self {
Self::ThreePrime => "3p",
Expand All @@ -19,6 +33,14 @@ impl AsRef<str> for Orientation {
}

impl Display for Orientation {
/// Obtain a formatted [`String`] representation of an [`Orientation`].
///
/// ```
/// # use pmd_mask::genome::Orientation;
/// assert_eq!(&format!("{: ^6}", Orientation::ThreePrime), " 3p ");
/// assert_eq!(&format!("{: <4}", Orientation::FivePrime), "5p ");
/// ```
///
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
Expand Down
1 change: 1 addition & 0 deletions src/genome/strand/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use thiserror::Error;

/// Error type associated with [`crate::genome::Strand`]
#[derive(Debug, Error, PartialEq)]
pub enum StrandError {
#[error("Failed to parse string value '{0}' into a valid Strand representation")]
Expand Down
60 changes: 57 additions & 3 deletions src/genome/strand/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ use rust_htslib::bam::Record;
mod error;
pub use error::StrandError;

/// Strand orientation representation for paired-end sequencing reads.
/// Strand orientation representation for paired-end sequencing reads.
///
/// Two possible variants:
/// - [`Strand::Forward`]|`'+'`
/// - [`Strand::Reverse`]|`'-'`
#[derive(Debug, Clone, Copy, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum Strand {
#[serde(rename = "+")] Forward,
Expand All @@ -15,7 +19,27 @@ pub enum Strand {


impl Strand {

/// Attempt to retrieve and the strand symbol of a [`htslib::bam::Record`](rust_htslib::bam::Record)
/// and parse it into a [`Strand`].
///
/// ```rust
/// use rust_htslib::bam::Record;
/// use pmd_mask::genome::{Strand, StrandError};
///
/// fn main() -> Result<(), StrandError> {
/// let mut r = Record::new();
/// assert_eq!(Strand::from_htslib_record(&mut r)?, Strand::Forward);
///
/// r.set_reverse();
/// assert_eq!(Strand::from_htslib_record(&mut r)?, Strand::Reverse);
/// Ok(())
/// }
/// ```
/// # Errors
///
/// Returns a [`StrandError::ParseFromHtsLib`] if the method ever fails to parse the
/// strand symbol given out from the bam record.
///
pub fn from_htslib_record(record: &mut Record) -> Result<Self, StrandError> {
record.strand()
.strand_symbol()
Expand All @@ -24,8 +48,13 @@ impl Strand {
}
}


impl AsRef<str> for Strand {
/// Return a [`str`] slice representation of the given [`Strand`].
/// ```
/// use pmd_mask::genome::Strand;
/// assert_eq!(Strand::Forward.as_ref(), "+");
/// assert_eq!(Strand::Reverse.as_ref(), "-");
/// ```
fn as_ref(&self) -> &str {
match self {
Self::Forward => "+",
Expand All @@ -34,8 +63,33 @@ impl AsRef<str> for Strand {
}
}

impl From<Strand> for char {
/// Convert a [`Strand`] into its primitive [`char`] representation
/// ```
/// use pmd_mask::genome::Strand;
///
/// let forward: char = Strand::Forward.into();
/// assert_eq!(forward, '+');
///
/// let reverse: char = Strand::Reverse.into();
/// assert_eq!(reverse, '-');
/// ```
fn from(value: Strand) -> Self {
match value {
Strand::Forward => '+',
Strand::Reverse => '-',
}
}
}

impl Display for Strand {
/// Return the String representation of [`Strand`].
/// ```
/// use pmd_mask::genome::Strand;
/// assert_eq!(format!("{: ^5}", Strand::Forward), " + ");
/// assert_eq!(format!("{: <5}", Strand::Reverse), "- ");
///
/// ```
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.as_ref().fmt(f)
}
Expand Down
Loading