Skip to main content

termal_alignment/seq/
fasta.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2025-2026 Thomas Junier
3
4use std::fs::File;
5use std::io::{BufRead, BufReader};
6use std::path::Path;
7
8use crate::seq::file::SeqFile;
9use crate::seq::record::SeqRecord;
10
11pub fn read_fasta_file<P: AsRef<Path>>(path: P) -> Result<SeqFile, std::io::Error> {
12    let file = File::open(path)?;
13    let mut result: SeqFile = Vec::new();
14    let mut current_record = SeqRecord {
15        header: String::new(),
16        sequence: String::new(),
17    };
18    let mut first_header = true;
19
20    for line in BufReader::new(file).lines() {
21        let l: String = line.unwrap();
22        if let Some(hdr) = l.strip_prefix(">") {
23            if first_header {
24                first_header = false;
25            } else {
26                // push existing record
27                result.push(current_record);
28            }
29            current_record = SeqRecord {
30                header: String::new(),
31                sequence: String::new(),
32            };
33            current_record.header.push_str(hdr);
34        } else {
35            // append line to current record'd sequence
36            current_record.sequence.push_str(&l);
37        }
38    }
39    result.push(current_record);
40    Ok(result)
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn test_read_fasta_file_1() {
49        let path = "data/test1.fas";
50        let fasta: SeqFile = read_fasta_file(path).expect("Test file not found");
51        assert_eq!(fasta[0].header, "seq1");
52        assert_eq!(fasta[0].sequence, "GAATTC");
53    }
54
55    #[test]
56    fn test_read_fasta_file_2() {
57        let path = "data/test2.fas";
58        let fasta: SeqFile = read_fasta_file(path).expect("Test file not found");
59        assert_eq!(fasta[0].header, "seq1");
60        assert_eq!(fasta[0].sequence, "TTGCCG-CGA");
61        assert_eq!(fasta[1].header, "seq2");
62        assert_eq!(fasta[1].sequence, "TTCCCGGCGA");
63        assert_eq!(fasta[2].header, "seq3");
64        assert_eq!(fasta[2].sequence, "TTACCG-CAA");
65    }
66
67    #[test]
68    fn test_read_fasta_file_3() {
69        let path = "data/test3.pep";
70        let fasta: SeqFile = read_fasta_file(path).expect("Test file not found");
71        assert_eq!(
72            fasta[0].header,
73            "Some larger FastA record, with several lines"
74        );
75        assert_eq!(fasta[0].sequence, "HWYQYDSWSWHQIQDPWVASLMTGSEHNTTIVDLNVLGAMDCLWLCYCQPECFEVFSLCIEVDLPSCCWAKALCAFHMWDSMAKQCWMPEMGEVSYFYALSMFHYFLLHSRPIQPWQTHHIPYDSIVVDLIANYFYNMIVQDVDKNSNIRFDRSVMRDVMIYEFENTYATGVVFNVNGKCGQFCKNMIYVGTIETQKEYEMFKNLDCAVQKRHNLQPNCENIAMKMRIQYNGKRFRMDYWERYRCNDIKQVLPQPFTEVAMEHRTFKLWPTTRLMMSNPKCRQCLEWAAVETGWIFTTNF");
76    }
77}