libwfa2 0.1.1

Bindings to the C implementation of WFA2-lib
Documentation
use wfa::wavefront_aligner_set_max_alignment_steps;

use crate::bindings::*;
use core::slice;

#[derive(Debug, Clone)]
pub enum HeuristicStrategy {
    None,
    BandedStatic{ band_min_k: std::os::raw::c_int, band_max_k: std::os::raw::c_int },
    BandedAdaptive{ band_min_k: std::os::raw::c_int, band_max_k: std::os::raw::c_int, score_steps: std::os::raw::c_int },
    WFAdaptive{ min_wavefront_length: std::os::raw::c_int, max_distance_threshold: std::os::raw::c_int, score_steps: std::os::raw::c_int },
    XDrop{ xdrop: std::os::raw::c_int, score_steps: std::os::raw::c_int},
    ZDrop{ zdrop: std::os::raw::c_int, score_steps: std::os::raw::c_int},
    WFMash{ min_wavefront_length: std::os::raw::c_int, max_distance_threshold: std::os::raw::c_int, score_steps: std::os::raw::c_int },
}

#[derive(Debug, Clone)]
pub enum AlignmentScope {
    ComputeScore,
    Alignment,
    Undefined,
}

impl AlignmentScope {
    pub fn from_scope(val: wfa::alignment_scope_t) -> Self {
        match val {
            v if v == wfa::alignment_scope_t_compute_alignment => Self::Alignment,
            v if v == wfa::alignment_scope_t_compute_score => Self::ComputeScore,
            _ => Self::Undefined,
        }
    }
}


#[derive(Debug, Clone)]
pub enum AlignmentSpan {
    End2End,
    EndsFree{ pattern_begin_free: std::os::raw::c_int, pattern_end_free: std::os::raw::c_int, text_begin_free: std::os::raw::c_int, text_end_free: std::os::raw::c_int },
    Undefined,
}

impl AlignmentSpan {
    pub fn from_form(form: wfa::alignment_form_t) -> Self {
        match form.span {
            v if v == wfa::alignment_span_t_alignment_end2end => { Self::End2End },
            v if v == wfa::alignment_span_t_alignment_endsfree => {
                Self::EndsFree { 
                    pattern_begin_free: form.pattern_begin_free, 
                    pattern_end_free: form.pattern_end_free, 
                    text_begin_free: form.text_begin_free, 
                    text_end_free: form.text_end_free, 
                }
            }
            _ => Self::Undefined,
        }
    }
}

#[derive(Debug, Clone)]
pub enum MemoryMode {
    High, Medium, Low, Ultralow, Undefined
}

impl MemoryMode {
    pub fn from_value(val: u32) -> Self {
        match val {
            v if v == wfa::wavefront_memory_t_wavefront_memory_high => { Self::High },
            v if v == wfa::wavefront_memory_t_wavefront_memory_med => { Self::Medium },
            v if v == wfa::wavefront_memory_t_wavefront_memory_low => { Self::Low },
            v if v == wfa::wavefront_memory_t_wavefront_memory_ultralow => { Self::Ultralow },
            _ => Self::Undefined,
        }
    }
}

#[derive(Debug, Clone)]
pub enum AlignmentStatus {
    Completed,
    Partial,
    MaxStepsReached,
    OOM,
    Unattainable,
    Undefined
}

impl From<std::os::raw::c_int> for AlignmentStatus {
    fn from(value: std::os::raw::c_int) -> Self {
        match value {
            v if v == 0 => AlignmentStatus::Completed,
            v if v == 1 => AlignmentStatus::Partial,
            v if v == -100 => AlignmentStatus::MaxStepsReached,
            v if v == -200 => AlignmentStatus::OOM,
            v if v == -300 => AlignmentStatus::Unattainable,
            _ => AlignmentStatus::Undefined,
        }
    }
}

pub struct AffineWavefronts {
    wf_aligner: *mut wfa::wavefront_aligner_t,
}

impl Clone for AffineWavefronts {
    fn clone(&self) -> Self {
        Self { wf_aligner: self.wf_aligner.clone() }
    }
}

impl Default for AffineWavefronts {
    fn default() -> Self {
        Self { 
            // null pointer means wavefront_aligner_new will use default attributes.
            wf_aligner: unsafe { wfa::wavefront_aligner_new(core::ptr::null_mut()) },
        }
    }
}

impl Drop for AffineWavefronts {
    fn drop(&mut self) {
        unsafe { wfa::wavefront_aligner_delete(self.wf_aligner); }
    }
}

impl AffineWavefronts {
    pub fn aligner_mut(&mut self) -> *mut wfa::wavefront_aligner_t {
        self.wf_aligner
    }

    pub fn aligner(&self) -> *const wfa::wavefront_aligner_t {
        self.wf_aligner
    }

    pub fn set_penalties(&mut self, match_: i32, mismatch: i32, gap_opening: i32, gap_extension: i32) {
        unsafe {
            (*self.wf_aligner).penalties.match_ = match_;
            (*self.wf_aligner).penalties.mismatch = mismatch;
            (*self.wf_aligner).penalties.gap_opening1 = gap_opening;
            (*self.wf_aligner).penalties.gap_extension1 = gap_extension;
        }
    }

    pub fn with_penalties(match_: i32, mismatch: i32, gap_opening: i32, gap_extension: i32) -> Self {
        let mut s = Self {
            wf_aligner: unsafe { wfa::wavefront_aligner_new(core::ptr::null_mut()) },
        };
        unsafe {
            (*s.wf_aligner).penalties.match_ = match_;
            (*s.wf_aligner).penalties.mismatch = mismatch;
            (*s.wf_aligner).penalties.gap_opening1 = gap_opening;
            (*s.wf_aligner).penalties.gap_extension1 = gap_extension;
        }
        
        s
    }

    pub fn set_heuristic(&mut self, heuristic: &HeuristicStrategy) {
        match *heuristic {
            HeuristicStrategy::None => unsafe { wfa::wavefront_aligner_set_heuristic_none(self.wf_aligner) },
            HeuristicStrategy::BandedStatic { band_min_k, band_max_k } => unsafe { wfa::wavefront_aligner_set_heuristic_banded_static(self.wf_aligner, band_min_k, band_max_k) },
            HeuristicStrategy::BandedAdaptive { band_min_k, band_max_k, score_steps } => unsafe { wfa::wavefront_aligner_set_heuristic_banded_adaptive(self.wf_aligner, band_min_k, band_max_k, score_steps) },
            HeuristicStrategy::WFAdaptive { min_wavefront_length, max_distance_threshold, score_steps } => unsafe { wfa::wavefront_aligner_set_heuristic_wfadaptive(self.wf_aligner, min_wavefront_length, max_distance_threshold, score_steps) },
            HeuristicStrategy::XDrop { xdrop, score_steps } => unsafe { wfa::wavefront_aligner_set_heuristic_xdrop(self.wf_aligner, xdrop, score_steps) },
            HeuristicStrategy::ZDrop { zdrop, score_steps } => unsafe { wfa::wavefront_aligner_set_heuristic_zdrop(self.wf_aligner, zdrop, score_steps) },
            HeuristicStrategy::WFMash { min_wavefront_length, max_distance_threshold , score_steps} => unsafe { wfa::wavefront_aligner_set_heuristic_wfmash(self.wf_aligner, min_wavefront_length, max_distance_threshold, score_steps) },
        }
    }

    pub fn get_heuristics(&self) -> Vec::<HeuristicStrategy> {
        let mut hs = Vec::new();
        let heuristic = unsafe {*self.wf_aligner}.heuristic;
        let strategy = heuristic.strategy;

        if strategy & wfa::wf_heuristic_strategy_wf_heuristic_zdrop > 0 {
            hs.push(HeuristicStrategy::ZDrop { zdrop: heuristic.zdrop, score_steps: heuristic.steps_between_cutoffs });
        }
        if strategy & wfa::wf_heuristic_strategy_wf_heuristic_xdrop > 0 {
            hs.push(HeuristicStrategy::XDrop { xdrop: heuristic.zdrop, score_steps: heuristic.steps_between_cutoffs });
        }
        if strategy & wfa::wf_heuristic_strategy_wf_heuristic_banded_adaptive > 0 {
            hs.push(HeuristicStrategy::BandedAdaptive { band_min_k: heuristic.min_k, band_max_k: heuristic.max_k, score_steps: heuristic.steps_between_cutoffs });
        }
        if strategy & wfa::wf_heuristic_strategy_wf_heuristic_banded_static > 0 {
            hs.push(HeuristicStrategy::BandedStatic { band_min_k: heuristic.min_k, band_max_k: heuristic.max_k });
        }
        if strategy & wfa::wf_heuristic_strategy_wf_heuristic_wfadaptive > 0 {
            hs.push(HeuristicStrategy::WFAdaptive { min_wavefront_length: heuristic.min_wavefront_length, max_distance_threshold: heuristic.max_distance_threshold, score_steps: heuristic.steps_between_cutoffs });
        }
        if strategy & wfa::wf_heuristic_strategy_wf_heuristic_wfmash > 0 {
            hs.push(HeuristicStrategy::WFMash { min_wavefront_length: heuristic.min_wavefront_length, max_distance_threshold: heuristic.max_distance_threshold, score_steps: heuristic.steps_between_cutoffs });
        }
        hs
    }

    pub fn set_alignment_scope(&mut self, scope: AlignmentScope) {
        (unsafe { *self.wf_aligner }).alignment_scope = match scope {
            AlignmentScope::ComputeScore => wfa::alignment_scope_t_compute_score,
            AlignmentScope::Alignment => wfa::alignment_scope_t_compute_alignment,
            AlignmentScope::Undefined => panic!("Cannot set an undefined scope"),
        }
    }

    pub fn get_alignment_scope(&self) -> AlignmentScope {
        let a = unsafe { *self.wf_aligner };
        AlignmentScope::from_scope(a.alignment_scope)
    }

    pub fn set_alignment_span(&mut self, span: AlignmentSpan) {
        let form: &mut wfa::alignment_form_t = &mut (unsafe { *self.wf_aligner }).alignment_form;
        match span {
            AlignmentSpan::End2End => {                 
                unsafe { wfa::wavefront_aligner_set_alignment_end_to_end(self.wf_aligner) };
            },
            AlignmentSpan::EndsFree { pattern_begin_free, pattern_end_free, text_begin_free, text_end_free } => {
                unsafe { wfa::wavefront_aligner_set_alignment_free_ends(
                    self.wf_aligner, 
                    pattern_begin_free, 
                    pattern_end_free, 
                    text_begin_free, 
                    text_end_free) };
            },
            AlignmentSpan::Undefined => (),
        }
    }

    pub fn set_memory_mode(&mut self, mode: MemoryMode) {
        (unsafe { *self.wf_aligner }).memory_mode = match mode {
            MemoryMode::High => wfa::wavefront_memory_t_wavefront_memory_high,
            MemoryMode::Medium => wfa::wavefront_memory_t_wavefront_memory_med,
            MemoryMode::Low => wfa::wavefront_memory_t_wavefront_memory_low,
            MemoryMode::Ultralow => wfa::wavefront_memory_t_wavefront_memory_ultralow,
            MemoryMode::Undefined => panic!("Cannot set Undefined memory mode!"),
        }
    }

    pub fn get_memory_mode(&self) -> MemoryMode {
        let a = unsafe { *self.aligner() };
        MemoryMode::from_value(a.memory_mode as u32)
    }

    pub fn get_alignment_span(&self) -> AlignmentSpan {
        let form = unsafe { *self.aligner() }.alignment_form;
        AlignmentSpan::from_form(form)
    }

    pub fn set_max_alignment_steps(&mut self, steps: i32) {
        unsafe {
            wavefront_aligner_set_max_alignment_steps(self.wf_aligner, steps);
        }
    }

    pub fn set_max_alignment_score(&mut self, score: i32) {
        self.set_max_alignment_steps(score);
    }

    pub fn get_max_alignment_steps(&self) -> i32 {
        let a = unsafe { *self.aligner() };
        a.system.max_alignment_steps
    }

    pub fn cigar(&self) -> &[u8] {
        unsafe {
            let cigar = (*self.wf_aligner).cigar;
            let ops = (*cigar).operations;
            let begin_offset = (*cigar).begin_offset;
            let end_offset = (*cigar).end_offset;
            let length = end_offset - begin_offset;
            
            let cigar_slice: &[u8] = std::slice::from_raw_parts((ops as *const u8).add(begin_offset as usize), length.try_into().unwrap());
            cigar_slice
        }
    }

    pub fn score(&self) -> i32 {
        unsafe {
            let cigar = (*self.wf_aligner).cigar;
            (*cigar).score
        }
    }

    pub fn align(&self, a: &[u8], b: &[u8]) -> AlignmentStatus {
        unsafe {
            let a = slice::from_raw_parts(a.as_ptr() as *const i8, a.len());
            let b = slice::from_raw_parts(b.as_ptr() as *const i8, b.len());

            let alignment_status: AlignmentStatus = wfa::wavefront_align(
                self.wf_aligner,
                a.as_ptr(),
                a.len() as i32,
                b.as_ptr(),
                b.len() as i32,
            ).into();

            alignment_status
        }
    }
}