1#![deny(unsafe_code)]
2#![allow(
3 clippy::must_use_candidate,
4 clippy::missing_panics_doc,
5 clippy::missing_errors_doc,
6 clippy::module_name_repetitions
7)]
8pub mod color;
9pub mod matcher;
10use lazy_static::lazy_static;
11use std::{borrow::Borrow, path::Path};
12
13pub const DNA_BASES: [u8; 5] = *b"ACGTN";
14pub const IUPAC_BASES: [u8; 15] = *b"AGCTYRWSKMDVHBN";
15pub const IUPAC_BASES_COMPLEMENT: [u8; 15] = *b"TCGARYWSMKHBDVN";
16
17lazy_static! {
18 pub static ref COMPLEMENT: [u8; 256] = {
19 let mut comp = [0; 256];
20 for (v, a) in comp.iter_mut().enumerate() {
21 *a = v as u8;
22 }
23 for (&a, &b) in IUPAC_BASES.iter().zip(IUPAC_BASES_COMPLEMENT.iter()) {
24 comp[a as usize] = b;
25 comp[a as usize + 32] = b + 32; }
27 comp
28 };
29}
30
31fn complement(a: u8) -> u8 {
32 COMPLEMENT[a as usize]
33}
34
35fn reverse_complement<C, T>(text: T) -> Vec<u8>
36where
37 C: Borrow<u8>,
38 T: IntoIterator<Item = C>,
39 T::IntoIter: DoubleEndedIterator,
40{
41 text.into_iter()
42 .rev()
43 .map(|a| complement(*a.borrow()))
44 .collect()
45}
46
47fn is_path_with_extension<P: AsRef<Path>>(p: &P, extensions: [&str; 2]) -> bool {
49 if let Some(ext) = p.as_ref().extension() {
50 match ext.to_str() {
51 Some(x) => extensions.contains(&x),
52 None => false,
53 }
54 } else {
55 false
56 }
57}
58
59const GZIP_EXTENSIONS: [&str; 2] = ["gz", "bgz"];
61
62pub fn is_gzip_path<P: AsRef<Path>>(p: &P) -> bool {
64 is_path_with_extension(p, GZIP_EXTENSIONS)
65}
66
67const FASTQ_EXTENSIONS: [&str; 2] = ["fastq", "fq"];
69
70pub fn is_fastq_path<P: AsRef<Path>>(p: &P) -> bool {
72 is_path_with_extension(p, FASTQ_EXTENSIONS)
73}
74
75#[cfg(test)]
77pub mod tests {
78 use crate::*;
79 use rstest::rstest;
80 use std::str;
81 use tempfile::TempDir;
82
83 #[rstest]
88 #[case("ACGT", "ACGT")] #[case("ACG", "CGT")] fn test_reverse_complement(#[case] seq: &str, #[case] expected: &str) {
91 let result = reverse_complement(seq.as_bytes());
92 let string_result = str::from_utf8(&result).unwrap();
93 assert_eq!(&string_result, &expected);
94 }
95
96 #[rstest]
101 #[case("test_fastq.fq.gz", true)] #[case("test_fastq.fq.bgz", true)] #[case("test_fastq.fq.tar", false)] fn test_is_gzip_path(#[case] file_name: &str, #[case] expected: bool) {
105 let dir = TempDir::new().unwrap();
106 let file_path = dir.path().join(file_name);
107 let result = is_gzip_path(&file_path);
108 assert_eq!(result, expected);
109 }
110 #[rstest]
115 #[case("test_fastq.fq", true)] #[case("test_fastq.fastq", true)] #[case("test_fastq.sam", false)] fn test_is_fastq_path(#[case] file_name: &str, #[case] expected: bool) {
119 let dir = TempDir::new().unwrap();
120 let file_path = dir.path().join(file_name);
121 let result = is_fastq_path(&file_path);
122 assert_eq!(result, expected);
123 }
124}