blul_core/use_cases/
write_blutils_output.rs1use 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,
25
26 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}