blul_core/use_cases/
write_blutils_output.rs

1use crate::{
2    domain::dtos::{
3        blast_builder::BlastBuilder,
4        blutils_output::BlutilsOutput,
5        consensus_result::{ConsensusResult, QueryWithConsensus},
6    },
7    use_cases::shared::write_or_append_to_file,
8};
9
10use mycelium_base::utils::errors::MappedErrors;
11use serde::{Deserialize, Serialize};
12use std::{
13    fs::{remove_file, File},
14    io::Write,
15    path::PathBuf,
16};
17use tracing::{info, warn};
18use uuid::Uuid;
19
20#[derive(Clone, Debug, Serialize, Deserialize, clap::ValueEnum)]
21#[serde(rename_all = "camelCase")]
22pub enum OutputFormat {
23    /// JSON format
24    Json,
25
26    /// JSONL format
27    Jsonl,
28}
29
30pub fn write_blutils_output(
31    results: Vec<ConsensusResult>,
32    config: Option<BlastBuilder>,
33    blutils_out_file: Option<String>,
34    out_format: OutputFormat,
35) -> Result<(), MappedErrors> {
36    let blutils_out_file = match blutils_out_file {
37        Some(file) => {
38            let mut path = PathBuf::from(file);
39            match out_format {
40                OutputFormat::Jsonl => {
41                    path.set_extension("jsonl");
42                }
43                OutputFormat::Json => {
44                    path.set_extension("json");
45                }
46            }
47
48            info!("");
49            info!("Blutils output file:");
50            info!("\t{:?}", path);
51            info!("");
52
53            if path.exists() {
54                match remove_file(path.clone()) {
55                    Err(err) => panic!("Could not remove file given {err}"),
56                    Ok(_) => warn!("Output file overwritten!"),
57                };
58            };
59
60            if let Some(parent) = path.parent() {
61                if !parent.exists() {
62                    match std::fs::create_dir_all(parent) {
63                        Err(err) => {
64                            panic!("Could not create directory given {err}")
65                        }
66                        Ok(_) => (),
67                    };
68                }
69            }
70
71            Some(path)
72        }
73        None => None,
74    };
75
76    let run_id = match config.to_owned() {
77        Some(c) => c.run_id,
78        None => Uuid::new_v4(),
79    };
80
81    let mut consensus_type_results = results.iter().fold(
82        Vec::<QueryWithConsensus>::new(),
83        |mut init, record| {
84            match record {
85                ConsensusResult::NoConsensusFound(res) => {
86                    init.push(QueryWithConsensus {
87                        query: res.query.to_owned(),
88                        taxon: None,
89                        run_id: Some(run_id),
90                    });
91                }
92                ConsensusResult::ConsensusFound(res) => {
93                    init.push(QueryWithConsensus {
94                        query: res.query.to_owned(),
95                        taxon: res.taxon.to_owned(),
96                        run_id: Some(run_id),
97                    })
98                }
99            };
100
101            init
102        },
103    );
104
105    consensus_type_results.sort_by(|a, b| a.query.cmp(&b.query));
106
107    let config = match config {
108        Some(config) => Some(BlastBuilder {
109            subject_reads: PathBuf::from(config.subject_reads)
110                .file_name()
111                .unwrap()
112                .to_str()
113                .unwrap()
114                .to_string(),
115            ..config
116        }),
117        None => None,
118    };
119
120    match out_format {
121        OutputFormat::Json => {
122            if let Some(output_file) = blutils_out_file {
123                let mut file = match File::create(output_file.to_owned()) {
124                    Err(err) => panic!(
125                        "Error on persist output results into {}: {err}",
126                        output_file.as_os_str().to_str().unwrap()
127                    ),
128                    Ok(res) => res,
129                };
130
131                match file.write_all(
132                    serde_json::to_string_pretty(&BlutilsOutput {
133                        results: consensus_type_results,
134                        config,
135                    })
136                    .unwrap()
137                    .as_bytes(),
138                ) {
139                    Err(err) => panic!(
140                        "Unexpected error on write config to output file: {err}"
141                    ),
142                    Ok(_) => (),
143                };
144
145                Ok(())
146            } else {
147                if let Err(err) = serde_json::to_writer(
148                    std::io::stdout().lock(),
149                    &BlutilsOutput {
150                        results: consensus_type_results,
151                        config,
152                    },
153                ) {
154                    panic!("Unexpected error on write JSON output: {err}");
155                } else {
156                    Ok(())
157                }
158            }
159        }
160        OutputFormat::Jsonl => {
161            if let Some(output_file) = blutils_out_file {
162                let (writer, file) =
163                    write_or_append_to_file(output_file.as_path());
164
165                writer(
166                    serde_json::to_string(&config).unwrap() + "\n",
167                    file.try_clone().expect(
168                        "Unexpected error detected on write blast result",
169                    ),
170                )?;
171
172                for record in &consensus_type_results {
173                    match writer(
174                        serde_json::to_string(&record).unwrap() + "\n",
175                        file.try_clone().expect(
176                            "Unexpected error detected on write blast result",
177                        ),
178                    ) {
179                        Err(err) => {
180                            panic!("Unexpected error on write JSONL output file: {err}",)
181                        }
182                        Ok(_) => (),
183                    }
184                }
185
186                Ok(())
187            } else {
188                let mut stdout = std::io::stdout();
189
190                if let Err(err) = serde_json::to_writer(stdout.lock(), &config)
191                {
192                    panic!("Unexpected error on write JSONL output: {err}");
193                }
194
195                stdout.write(b"\n").unwrap();
196
197                for record in &consensus_type_results {
198                    if let Err(err) =
199                        serde_json::to_writer(stdout.lock(), &record)
200                    {
201                        panic!("Unexpected error on write JSONL output: {err}");
202                    }
203
204                    stdout.write(b"\n").unwrap();
205                }
206
207                Ok(())
208            }
209        }
210    }
211}