rsedlib/
lib.rs

1use std::{ffi::CStr, ops::Deref};
2
3use edlib_sys::{edlibAlignmentToCigar, EdlibAlignConfig, EDLIB_STATUS_OK};
4use param::EdlibAlignParam;
5
6pub mod edlib_sys;
7
8pub mod param;
9pub mod utils;
10
11#[derive(Debug)]
12struct AlignResultGuard(edlib_sys::EdlibAlignResult);
13impl AlignResultGuard {
14    fn target_start_ends(&self) -> Vec<(usize, usize)> {
15        let mut start_ends = Vec::new();
16        if self.0.numLocations > 0 {
17            for i in 0..self.0.numLocations {
18                unsafe {
19                    let start = if self.startLocations.is_null() {
20                        0
21                    } else {
22                        *self.0.startLocations.add(i as usize)
23                    };
24                    let end = *self.0.endLocations.add(i as usize);
25                    start_ends.push((start as usize, end as usize));
26                }
27            }
28        }
29
30        start_ends
31    }
32}
33
34impl Deref for AlignResultGuard {
35    type Target = edlib_sys::EdlibAlignResult;
36    fn deref(&self) -> &Self::Target {
37        &self.0
38    }
39}
40
41impl From<edlib_sys::EdlibAlignResult> for AlignResultGuard {
42    fn from(result: edlib_sys::EdlibAlignResult) -> Self {
43        AlignResultGuard(result)
44    }
45}
46
47struct AlignCigarGuard(*mut i8);
48
49impl Deref for AlignCigarGuard {
50    type Target = *mut i8;
51    fn deref(&self) -> &Self::Target {
52        &self.0
53    }
54}
55
56impl Drop for AlignCigarGuard {
57    fn drop(&mut self) {
58        unsafe {
59            libc::free(self.0 as *mut std::ffi::c_void);
60        }
61    }
62}
63
64impl From<*mut i8> for AlignCigarGuard {
65    fn from(cigar_str: *mut i8) -> Self {
66        AlignCigarGuard(cigar_str)
67    }
68}
69
70impl Drop for AlignResultGuard {
71    fn drop(&mut self) {
72        unsafe {
73            edlib_sys::edlibFreeAlignResult(self.0);
74        }
75    }
76}
77
78#[derive(Debug)]
79pub struct EdlibAlignResult {
80    pub edit_distance: i32,
81    pub alphabet_length: i32,
82    pub locations: Vec<(usize, usize)>,
83    pub cigar: Option<String>,
84}
85
86pub fn edlib_align(
87    query: &[u8],
88    target: &[u8],
89    aln_param: &EdlibAlignParam,
90) -> Result<EdlibAlignResult, String> {
91    let config = EdlibAlignConfig {
92        k: aln_param.k(),
93        mode: match aln_param.mode() {
94            param::AlignMode::Global => edlib_sys::EdlibAlignMode_EDLIB_MODE_NW,
95            param::AlignMode::Prefix => edlib_sys::EdlibAlignMode_EDLIB_MODE_SHW,
96            param::AlignMode::Infix => edlib_sys::EdlibAlignMode_EDLIB_MODE_HW,
97        },
98        task: match aln_param.task() {
99            param::AlignTask::Distance => edlib_sys::EdlibAlignTask_EDLIB_TASK_DISTANCE,
100            param::AlignTask::Locations => edlib_sys::EdlibAlignTask_EDLIB_TASK_LOC,
101            param::AlignTask::Path => edlib_sys::EdlibAlignTask_EDLIB_TASK_PATH,
102        },
103        additionalEqualities: if aln_param.additional_eq_pairs().len() > 0 {
104            aln_param.additional_eq_pairs().as_ptr()
105        } else {
106            std::ptr::null()
107        },
108        additionalEqualitiesLength: aln_param.additional_eq_pairs().len() as i32,
109    };
110
111    let edlib_raw_res: AlignResultGuard = unsafe {
112        edlib_sys::edlibAlign(
113            query.as_ptr() as *const i8,
114            query.len() as i32,
115            target.as_ptr() as *const i8,
116            target.len() as i32,
117            config,
118        )
119        .into()
120    };
121
122    // println!("{:?}", edlib_raw_res);
123
124    let align_cigar_str = if aln_param.task() == param::AlignTask::Path
125        && aln_param.cigar_fmt() != param::CigarFmt::NoCigar
126        && edlib_raw_res.status == EDLIB_STATUS_OK as i32
127    {
128        let cigar_fmt = match aln_param.cigar_fmt() {
129            param::CigarFmt::Standard => edlib_sys::EdlibCigarFormat_EDLIB_CIGAR_STANDARD,
130            param::CigarFmt::Extended => edlib_sys::EdlibCigarFormat_EDLIB_CIGAR_EXTENDED,
131            _ => panic!("Invalid cigar format"),
132        };
133
134        unsafe {
135            let cigar_str_guard: AlignCigarGuard = edlibAlignmentToCigar(
136                edlib_raw_res.alignment,
137                edlib_raw_res.alignmentLength,
138                cigar_fmt,
139            )
140            .into();
141            Some(
142                CStr::from_ptr(*cigar_str_guard)
143                    .to_str()
144                    .unwrap()
145                    .to_string(),
146            )
147        }
148    } else {
149        None
150    };
151
152    if edlib_raw_res.status != EDLIB_STATUS_OK as i32 {
153        Err(format!(
154            "Edlib alignment failed with status: {}",
155            edlib_raw_res.status
156        ))
157    } else {
158        // edlib_raw_res.target_start_ends()
159        Ok(EdlibAlignResult {
160            edit_distance: edlib_raw_res.editDistance,
161            alphabet_length: edlib_raw_res.alphabetLength,
162            locations: edlib_raw_res.target_start_ends(),
163            cigar: align_cigar_str,
164        })
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use crate::utils::reverse_complement;
172
173    #[test]
174    fn test_edlib_align() {
175        let query = b"ATAATTTGTTCTCTGCGTTGACGTGCTTGAACCCTTAGCATCTTCTAAAATCTTTTTGACTTCATTCTTGTGTTTCCTTTAAGTTTTGTTATTTTCTTCTTCAAGTTCTGTGCAATTTAATAGCTTGTCTGCGTCATCGAATATCTCTGTTAATATCTCTTTCACGTTTATCCCATTACATCTTTCAGGCACCCCAATGCGAACTTTTTAAGTAACGCAAGAATAGCGATGAAATTAGGGACCC";
176
177        let query_rc = &reverse_complement(query);
178
179        let target = b"GGTCCTAACTTTCATCGAATTACTTGGCGTTACTTAAAAGTCGCATGGGTCCATTGAAAGATGTAATGGATAACGCTGAAAGAGATAATTAACAAGATATCGATGACGCAGAACAAGCTAAGTTAAATGGCACAGAAACCTTGAAGAAGAAATAAACCAAAAACTTAAGAAAGCCAAGAAAAGTTCAAAAAGATTTTTAAGAAGATGCTAAGGTTCAGCAGTCAACAGCAAGAAACAAATTTTAT";
180
181        let param = EdlibAlignParam::default();
182        let aln_res = edlib_align(query, target, &param);
183        println!("{:?}", aln_res);
184
185        let aln_res = edlib_align(query_rc, target, &param);
186        println!("{:?}", aln_res);
187    }
188
189    #[test]
190    fn test_edlib_align2() {
191        let query = b"elephant";
192        let target = b"telephone";
193
194        let mut param = EdlibAlignParam::default();
195        // param.set_mode(param::AlignMode::Global);
196        param.set_task(param::AlignTask::Path);
197        param.set_cigar_fmt(param::CigarFmt::Extended);
198        let aln_res = edlib_align(query, target, &param);
199        println!("{:?}", aln_res);
200    }
201}