1mod bigwig_sys;
2
3use bigwig_sys::*;
4use std::ffi::{CStr, CString};
5use std::os::unix::ffi::OsStrExt;
6use std::path::Path;
7
8pub struct BigWigFile(*mut bigWigFile_t);
9
10impl Drop for BigWigFile {
11 fn drop(&mut self) {
12 unsafe {
13 bwClose(self.0);
14 }
15 }
16}
17
18pub struct BigWigInterval {
19 pub begin: u32,
20 pub end: u32,
21 pub value: f32,
22}
23
24pub struct BigWigIntervalIter {
25 result: *mut bwOverlappingIntervals_t,
26 idx: isize,
27}
28
29impl Drop for BigWigIntervalIter {
30 fn drop(&mut self) {
31 unsafe {
32 bwDestroyOverlappingIntervals(self.result);
33 }
34 }
35}
36
37impl Iterator for BigWigIntervalIter {
38 type Item = BigWigInterval;
39 fn next(&mut self) -> Option<BigWigInterval> {
40 let size = unsafe { *self.result }.l;
41 if size as isize <= self.idx {
42 return None;
43 }
44 let begin = unsafe { *self.result.as_ref().unwrap().start.offset(self.idx) };
45 let end = unsafe { *self.result.as_ref().unwrap().end.offset(self.idx) };
46 let value = unsafe { *self.result.as_ref().unwrap().value.offset(self.idx) };
47
48 self.idx += 1;
49 Some(BigWigInterval { begin, end, value })
50 }
51}
52
53impl BigWigFile {
54 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, std::io::Error> {
55 let handle = unsafe {
56 let path_buf = CString::new(path.as_ref().as_os_str().as_bytes()).unwrap();
57 let mod_buf = CString::new("r").unwrap();
58 bwOpen(
59 path_buf.as_ptr() as *mut _,
60 None,
61 mod_buf.as_ptr() as *mut _,
62 )
63 };
64
65 if handle.is_null() {
66 return Err(std::io::Error::new(
67 std::io::ErrorKind::Other,
68 "Cannot open BW file",
69 ));
70 }
71 Ok(BigWigFile(handle))
72 }
73
74 pub fn chroms(&self) -> Vec<(String, usize)> {
75 let chrom_info = unsafe { (*self.0).cl.as_ref().unwrap() };
76
77 let mut ret = Vec::with_capacity(chrom_info.nKeys as usize);
78
79 for i in 0..chrom_info.nKeys as isize {
80 let (name, size) = unsafe {
81 let ptr = *chrom_info.chrom.offset(i);
82 let str = CStr::from_ptr(ptr);
83 let name = str.to_string_lossy();
84
85 let size = *chrom_info.len.offset(i) as usize;
86 (name.to_string(), size)
87 };
88
89 ret.push((name, size));
90 }
91 ret
92 }
93
94 pub fn query_range(&self, chrom: &str, left: u32, right: u32) -> Option<BigWigIntervalIter> {
95 let str = CString::new(chrom).unwrap();
96 let chrom = str.as_ptr();
97 let handle = unsafe { bwGetOverlappingIntervals(self.0 as _, chrom as _, left, right) };
98 if handle.is_null() {
99 return None;
100 }
101 Some(BigWigIntervalIter {
102 result: handle,
103 idx: 0,
104 })
105 }
106}