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    /// Yaml format
30    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}