polaranges 0.3.2

Rust-first genomic range operations on top of Polars DataFrames
Documentation
use std::path::PathBuf;

use polars_core::prelude::PlSmallStr;

pub const DEFAULT_CHROM_COL: &str = "Chromosome";
pub const DEFAULT_START_COL: &str = "Start";
pub const DEFAULT_END_COL: &str = "End";
pub const DEFAULT_STRAND_COL: &str = "Strand";
pub const DEFAULT_NEAREST_SUFFIX: &str = "_b";
pub const DEFAULT_DISTANCE_COL: &str = "Distance";

/// Shared operation specs sit between CLI parsing and execution so both the
/// modern regular mode and a future compat translator can reuse the same engine.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct IntervalColumns {
    pub chrom_col: PlSmallStr,
    pub start_col: PlSmallStr,
    pub end_col: PlSmallStr,
    pub strand_col: Option<PlSmallStr>,
}

impl Default for IntervalColumns {
    fn default() -> Self {
        Self {
            chrom_col: DEFAULT_CHROM_COL.into(),
            start_col: DEFAULT_START_COL.into(),
            end_col: DEFAULT_END_COL.into(),
            strand_col: Some(DEFAULT_STRAND_COL.into()),
        }
    }
}

impl IntervalColumns {
    pub fn required_columns(&self, strand_constraint: StrandConstraint) -> Vec<&str> {
        let mut required = vec![
            self.chrom_col.as_str(),
            self.start_col.as_str(),
            self.end_col.as_str(),
        ];
        if strand_constraint.requires_strand() {
            if let Some(strand_col) = self.strand_col.as_ref() {
                required.push(strand_col.as_str());
            }
        }
        required
    }

    pub fn resolved_strand_col(&self) -> PlSmallStr {
        self.strand_col
            .clone()
            .unwrap_or_else(|| DEFAULT_STRAND_COL.into())
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BinaryInputs {
    pub left_path: PathBuf,
    pub right_path: PathBuf,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StrandConstraint {
    Ignore,
    Same,
    Opposite,
}

impl StrandConstraint {
    pub fn requires_strand(self) -> bool {
        !matches!(self, Self::Ignore)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OverlapCommandSpec {
    pub inputs: BinaryInputs,
    pub columns: IntervalColumns,
    pub strand_constraint: StrandConstraint,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct NearestCommandSpec {
    pub inputs: BinaryInputs,
    pub columns: IntervalColumns,
    pub strand_constraint: StrandConstraint,
    pub suffix: String,
    pub distance_column: Option<String>,
    pub preserve_input_order: bool,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum IntervalCommandSpec {
    Overlap(OverlapCommandSpec),
    Nearest(NearestCommandSpec),
}