1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use std::convert::AsRef;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Cursor, Write};
use std::path::Path;

use util::ResultType;

/// Serialize to and from FASTQ.
///
/// # Serialized Format
///
/// ```text
/// @SRR390728.1 1 length=72
/// CATTCTTCACGTAGTTCTCGAGCCTTGGTTTTCAGCGATGGAGAATGACTTTGACAAGCTGAGAGAAGNTNC
/// +SRR390728.1 1 length=72
/// ;;;;;;;;;;;;;;;;;;;;;;;;;;;9;;665142;;;;;;;;;;;;;;;;;;;;;;;;;;;;;96&&&&(
/// ```
pub trait Fastq: Sized {
    /// Estimate the size of the resulting FASTQ output to avoid reallocations.
    #[inline(always)]
    fn estimate_fastq_size(&self) -> usize {
        0
    }

    /// Export model to FASTQ.
    ///
    /// Note that many small writers are made to the writer, so the writer
    /// should be buffered.
    fn to_fastq<T: Write>(&self, writer: &mut T) -> ResultType<()>;

    /// Export model to FASTQ string.
    fn to_fastq_string(&self) -> ResultType<String> {
        let capacity = self.estimate_fastq_size();
        let mut writer = Cursor::new(Vec::with_capacity(capacity));

        self.to_fastq(&mut writer)?;
        match String::from_utf8(writer.into_inner()) {
            Err(e)  => Err(Box::new(e)),
            Ok(v)   => Ok(v),
        }
    }

    /// Export model to FASTQ output file.
    #[inline]
    fn to_fastq_file<P: AsRef<Path>>(&self, path: P) -> ResultType<()> {
        let file = File::create(path)?;
        let mut writer = BufWriter::new(file);
        self.to_fastq(&mut writer)
    }

    /// Import model from FASTQ.
    fn from_fastq<T: BufRead>(reader: &mut T) -> ResultType<Self>;

    /// Import model from FASTQ string.
    #[inline]
    fn from_fastq_string(text: &str) -> ResultType<Self> {
        // Rust uses the contents of the immutable &str as the buffer
        // Cursor is then immutable.
        let mut reader = Cursor::new(text);
        Self::from_fastq(&mut reader)
    }

    /// Import model from FASTQ file.
    #[inline]
    fn from_fastq_file<P: AsRef<Path>>(path: P) -> ResultType<Self> {
        let file = File::open(path)?;
        let mut reader = BufReader::new(file);
        Self::from_fastq(&mut reader)
    }
}

/// Specialization of the `Fastq` trait for collections.
pub trait FastqCollection: Fastq {
    /// Export collection to FASTQ.
    ///
    /// Returns an error if any of the items within the collection
    /// are invalid.
    ///
    /// Note that many small writers are made to the writer, so the writer
    /// should be buffered.
    fn to_fastq_strict<T: Write>(&self, writer: &mut T) -> ResultType<()>;

    /// Export collection to FASTQ.
    ///
    /// Returns only errors due to serialization issues, otherwise,
    /// exports as many items as possible.
    ///
    /// Note that many small writers are made to the writer, so the writer
    /// should be buffered.
    fn to_fastq_lenient<T: Write>(&self, writer: &mut T) -> ResultType<()>;

    /// Import collection from FASTQ.
    ///
    /// Returns an error if any of the items within the FASTQ document
    /// are invalid.
    fn from_fastq_strict<T: BufRead>(reader: &mut T) -> ResultType<Self>;

    /// Import collection from FASTQ.
    ///
    /// Returns only errors due to deserialization errors, otherwise,
    /// imports as many items as possible.
    fn from_fastq_lenient<T: BufRead>(reader: &mut T) -> ResultType<Self>;
}