brrrr_lib/
json_writer.rs

1// (c) Copyright 2020 Trent Hauck
2// All Rights Reserved
3/// The `json_writer` module provides an implementation for the `RecordWriter` interface to read
4/// and write from json.
5use std::io::{self, BufRead, ErrorKind, Write};
6
7use serde::ser::Serialize;
8
9use crate::errors::BrrrrError;
10use crate::types::FastaRecord;
11use crate::types::FastqRecord;
12use crate::types::GffRecord;
13use crate::writer;
14
15use writer::RecordWriter;
16
17use noodles::fasta;
18use noodles::fastq;
19use noodles::gff;
20
21/// JsonRecordWriter holds a writer, and outputs FASTA records as newline delimited json.
22pub struct JsonRecordWriter<W: Write> {
23    writer: W,
24}
25
26impl<W: Write> JsonRecordWriter<W> {
27    /// Creates a new JsonRecordWriter with a writer.
28    pub fn new(w: W) -> Self {
29        Self { writer: w }
30    }
31}
32
33impl<W: Write> writer::RecordWriter for JsonRecordWriter<W> {
34    /// Writes an input FASTA to the underlying writer.
35    fn write_serde_record<S: Serialize>(&mut self, r: S) -> io::Result<()> {
36        serde_json::to_writer(&mut self.writer, &r)?;
37        self.writer.write_all(b"\n")?;
38
39        Ok(())
40    }
41}
42
43/// Converts a FASTQ file to JSONL
44///
45/// # Arguments
46///
47/// * `input` an input that implements the BufRead trait.
48/// * `output` an output that implements the Write trait.
49pub fn fq2jsonl<R: BufRead, W: Write>(input: R, output: &mut W) -> Result<(), BrrrrError> {
50    let mut reader = fastq::Reader::new(input);
51    let record_writer = &mut JsonRecordWriter::new(output);
52
53    for read_record in reader.records() {
54        let record = read_record?;
55        let write_op = record_writer.write_serde_record(FastqRecord::from(record));
56
57        if let Err(e) = write_op {
58            match e.kind() {
59                ErrorKind::BrokenPipe => break,
60                _ => return Err(BrrrrError::from(e)),
61            }
62        }
63    }
64    Ok(())
65}
66
67/// Converts a FASTA to JSONL
68///
69/// # Arguments
70///
71/// * `input` an input that implements the Read trait.
72/// * `output` an output that implements the Write trait.
73pub fn fa2jsonl<R: BufRead, W: Write>(input: R, output: &mut W) -> Result<(), BrrrrError> {
74    let mut reader = fasta::Reader::new(input);
75    let record_writer = &mut JsonRecordWriter::new(output);
76
77    for read_record in reader.records() {
78        let record = read_record?;
79        let write_op = record_writer.write_serde_record(FastaRecord::from(record));
80
81        if let Err(e) = write_op {
82            match e.kind() {
83                ErrorKind::BrokenPipe => break,
84                _ => return Err(BrrrrError::from(e)),
85            }
86        }
87    }
88    Ok(())
89}
90
91/// Converts a GFF file to JSONL
92///
93/// # Arguments
94///
95/// * `input` an input that implements the Read trait.
96/// * `output` an output that implements the Write trait.
97pub fn gff2jsonl<R: BufRead, W: Write>(input: R, output: &mut W) -> Result<(), BrrrrError> {
98    let mut reader = gff::Reader::new(input);
99    let record_writer = &mut JsonRecordWriter::new(output);
100
101    for read_record in reader.records() {
102        let record = read_record?;
103        let write_op = record_writer.write_serde_record(GffRecord::from(record));
104
105        if let Err(e) = write_op {
106            match e.kind() {
107                ErrorKind::BrokenPipe => break,
108                _ => return Err(BrrrrError::from(e)),
109            }
110        }
111    }
112    Ok(())
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[test]
120    fn test_fa2jsonl() {
121        let input = b">A\nATCG\n" as &[u8];
122
123        let mut output = Vec::new();
124        fa2jsonl(input, &mut output).unwrap();
125
126        let output_str = String::from_utf8(output).unwrap();
127        let expected_output = "{\"id\":\"A\",\"desc\":null,\"seq\":\"ATCG\"}\n".to_string();
128        assert_eq!(output_str, expected_output);
129    }
130}