1#[cfg(feature = "maf")]
3pub mod bed;
4pub mod delimited;
5pub mod fasta;
6#[cfg(feature = "maf")]
7pub mod maf;
8pub mod nexus;
9pub mod partition;
10pub mod phylip;
11#[cfg(feature = "read")]
12pub mod qscores;
13pub mod txt;
14
15#[cfg(target_os = "windows")]
16const CAR_RETURN: u8 = b'\r';
17
18#[cfg(feature = "maf")]
19const END_OF_LINE: u8 = b'\n';
20#[cfg(feature = "maf")]
21const EOF: usize = 0;
22
23pub(crate) const MAX_RESERVE: usize = 1 << 30;
26
27pub(crate) fn split_id_seq(line: &str) -> Option<(&str, &str)> {
33 let line = line.trim();
34 let split = line.bytes().position(|b| b.is_ascii_whitespace())?;
35 let (id, seq) = (&line[..split], line[split..].trim_start());
36 if has_whitespace(seq.as_bytes()) {
37 return None;
38 }
39 Some((id, seq))
40}
41
42fn has_whitespace(bytes: &[u8]) -> bool {
43 bytes
47 .chunks(64)
48 .any(|c| c.iter().fold(false, |acc, &b| acc | (b <= b' ')))
49 && bytes.iter().any(|b| b.is_ascii_whitespace())
50}
51
52macro_rules! insert_matrix {
53 ($self: ident, $id: ident, $seq: ident ) => {
54 match $self.matrix.get($id) {
55 Some(original_seq) => panic!(
56 "Found duplicate IDs for file {}. \
57 First duplicate ID found: {}. \
58 Both sequences are the same: {}.",
59 $self.input.display(),
60 $id,
61 original_seq == $seq
62 ),
63 None => {
64 $self.matrix.insert($id.to_string(), $seq.to_string());
65 }
66 }
67 };
68}
69
70macro_rules! warn_duplicate_ids {
71 ($self: ident, $ids: ident) => {
72 if $ids.len() != $self.header.ntax {
73 log::warn!(
74 "{} Found problematic alignment: {}.\n\
75 Number of taxa in the matrix ({} taxa) \
76 does not match the number of taxa in the header ({} taxa).\n\
77 If this is `id` subcommand, try to use the same file as an input \
78 for other subcommands.\n\
79 If the cause is duplicate IDs, \
80 the other subcommands will show the first found duplicate.\n\n",
81 "WARNING!".red(),
82 $self.input.display(),
83 $ids.len(),
84 $self.header.ntax
85 );
86 }
87 };
88}
89
90pub(crate) use insert_matrix;
91pub(crate) use warn_duplicate_ids;
92
93#[cfg(test)]
94mod test {
95 use super::*;
96
97 #[test]
98 fn test_split_id_seq() {
99 assert_eq!(Some(("ABCD", "ACGT")), split_id_seq("ABCD ACGT"));
100 assert_eq!(Some(("ABCD", "ACGT")), split_id_seq(" ABCD \t ACGT\r\n"));
101 assert_eq!(None, split_id_seq("AB CD ACGT"));
102 assert_eq!(None, split_id_seq("ABCD ACG T"));
103 assert_eq!(None, split_id_seq("ABCD"));
104 assert_eq!(None, split_id_seq(""));
105 }
106}