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 Yaml,
31}
32
33pub fn write_blutils_output(
34 results: Vec<ConsensusResult>,
35 config: Option<BlastBuilder>,
36 blutils_out_file: Option<String>,
37 out_format: OutputFormat,
38) -> Result<(), MappedErrors> {
39 let blutils_out_file = match blutils_out_file {
40 Some(file) => {
41 let mut path = PathBuf::from(file);
42 match out_format {
43 OutputFormat::Jsonl => {
44 path.set_extension("jsonl");
45 }
46 OutputFormat::Json => {
47 path.set_extension("json");
48 }
49 OutputFormat::Yaml => {
50 path.set_extension("yaml");
51 }
52 }
53
54 info!("");
55 info!("Blutils output file:");
56 info!("\t{:?}", path);
57 info!("");
58
59 if path.exists() {
60 match remove_file(path.clone()) {
61 Err(err) => panic!("Could not remove file given {err}"),
62 Ok(_) => warn!("Output file overwritten!"),
63 };
64 };
65
66 if let Some(parent) = path.parent() {
67 if !parent.exists() {
68 match std::fs::create_dir_all(parent) {
69 Err(err) => {
70 panic!("Could not create directory given {err}")
71 }
72 Ok(_) => (),
73 };
74 }
75 }
76
77 Some(path)
78 }
79 None => None,
80 };
81
82 let run_id = match config.to_owned() {
83 Some(c) => c.run_id,
84 None => Uuid::new_v4(),
85 };
86
87 let mut consensus_type_results = results.iter().fold(
88 Vec::<QueryWithConsensus>::new(),
89 |mut init, record| {
90 match record {
91 ConsensusResult::NoConsensusFound(res) => {
92 init.push(QueryWithConsensus {
93 query: res.query.to_owned(),
94 taxon: None,
95 run_id: Some(run_id),
96 });
97 }
98 ConsensusResult::ConsensusFound(res) => {
99 init.push(QueryWithConsensus {
100 query: res.query.to_owned(),
101 taxon: res.taxon.to_owned(),
102 run_id: Some(run_id),
103 })
104 }
105 };
106
107 init
108 },
109 );
110
111 consensus_type_results.sort_by(|a, b| a.query.cmp(&b.query));
112
113 let config = match config {
114 Some(config) => Some(BlastBuilder {
115 subject_reads: PathBuf::from(config.subject_reads)
116 .file_name()
117 .unwrap()
118 .to_str()
119 .unwrap()
120 .to_string(),
121 ..config
122 }),
123 None => None,
124 };
125
126 match out_format {
127 OutputFormat::Json => {
128 if let Some(output_file) = blutils_out_file {
129 let mut file = match File::create(output_file.to_owned()) {
130 Err(err) => panic!(
131 "Error on persist output results into {}: {err}",
132 output_file.as_os_str().to_str().unwrap()
133 ),
134 Ok(res) => res,
135 };
136
137 match file.write_all(
138 serde_json::to_string_pretty(&BlutilsOutput {
139 results: consensus_type_results,
140 config,
141 })
142 .unwrap()
143 .as_bytes(),
144 ) {
145 Err(err) => panic!(
146 "Unexpected error on write config to output file: {err}"
147 ),
148 Ok(_) => (),
149 };
150
151 Ok(())
152 } else {
153 if let Err(err) = serde_json::to_writer(
154 std::io::stdout().lock(),
155 &BlutilsOutput {
156 results: consensus_type_results,
157 config,
158 },
159 ) {
160 panic!("Unexpected error on write JSON output: {err}");
161 } else {
162 Ok(())
163 }
164 }
165 }
166 OutputFormat::Jsonl => {
167 if let Some(output_file) = blutils_out_file {
168 let (writer, file) =
169 write_or_append_to_file(output_file.as_path());
170
171 writer(
172 serde_json::to_string(&config).unwrap() + "\n",
173 file.try_clone().expect(
174 "Unexpected error detected on write blast result",
175 ),
176 )?;
177
178 for record in &consensus_type_results {
179 match writer(
180 serde_json::to_string(&record).unwrap() + "\n",
181 file.try_clone().expect(
182 "Unexpected error detected on write blast result",
183 ),
184 ) {
185 Err(err) => {
186 panic!("Unexpected error on write JSONL output file: {err}",)
187 }
188 Ok(_) => (),
189 }
190 }
191
192 Ok(())
193 } else {
194 let mut stdout = std::io::stdout();
195
196 if let Err(err) = serde_json::to_writer(stdout.lock(), &config)
197 {
198 panic!("Unexpected error on write JSONL output: {err}");
199 }
200
201 stdout.write(b"\n").unwrap();
202
203 for record in &consensus_type_results {
204 if let Err(err) =
205 serde_json::to_writer(stdout.lock(), &record)
206 {
207 panic!("Unexpected error on write JSONL output: {err}");
208 }
209
210 stdout.write(b"\n").unwrap();
211 }
212
213 Ok(())
214 }
215 }
216 OutputFormat::Yaml => {
217 if let Some(output_file) = blutils_out_file {
218 let file = match File::create(output_file.to_owned()) {
219 Err(err) => panic!(
220 "Error on persist output results into {}: {err}",
221 output_file.as_os_str().to_str().unwrap()
222 ),
223 Ok(res) => res,
224 };
225
226 serde_yaml::to_writer(
227 file,
228 &BlutilsOutput {
229 results: consensus_type_results,
230 config,
231 },
232 )
233 .unwrap();
234
235 Ok(())
236 } else {
237 serde_yaml::to_writer(
238 std::io::stdout().lock(),
239 &BlutilsOutput {
240 results: consensus_type_results,
241 config,
242 },
243 )
244 .unwrap();
245
246 Ok(())
247 }
248 }
249 }
250}