serde_hkx_features/convert/
rayon.rs

1//! Parallel Convert hkx <-> xml
2
3use super::{OutFormat, get_output_path, get_supported_files, process_serde};
4use crate::{
5    error::{FailedConvertFilesSnafu, Result},
6    fs::write_sync,
7    progress::ProgressHandler,
8};
9use rayon::prelude::*;
10use std::{
11    fs,
12    io::{self},
13    path::{Path, PathBuf},
14};
15
16/// Convert dir or file (hkx, xml).
17///
18/// # Note
19/// If `output` is not specified, the output is placed at the same level as `input`.
20///
21/// # Errors
22/// Failed to convert.
23pub fn convert_progress<I, O, P>(
24    input: I,
25    output: Option<O>,
26    format: OutFormat,
27    progress: P,
28) -> Result<()>
29where
30    I: AsRef<Path>,
31    O: AsRef<Path> + Sync,
32    P: ProgressHandler + Sync,
33{
34    let input = input.as_ref();
35    if input.is_dir() {
36        convert_dir(input, output, format, progress)?;
37    } else if input.is_file() {
38        convert_file(input, output, format)?;
39    } else {
40        Err(io::Error::new(
41            io::ErrorKind::NotFound,
42            format!("The path does not exist: {}", input.display()),
43        ))?;
44    }
45
46    Ok(())
47}
48
49/// Convert directory.
50///
51/// # Note
52/// If `output` is not specified, the output is placed at the same level as `input`.
53///
54/// # Errors
55/// Failed to convert.
56pub fn convert_dir<I, O, P>(
57    input_dir: I,
58    output_dir: Option<O>,
59    format: OutFormat,
60    mut progress: P,
61) -> Result<()>
62where
63    I: AsRef<Path>,
64    O: AsRef<Path> + Sync,
65    P: ProgressHandler + Sync,
66{
67    let input_dir = input_dir.as_ref();
68    let files: Vec<PathBuf> = get_supported_files(input_dir);
69
70    if files.is_empty() {
71        progress.on_empty();
72        return Ok(());
73    }
74
75    let total_files = files.len();
76    progress.on_set_total(total_files);
77
78    let err_paths: Vec<PathBuf> = files
79        .into_par_iter()
80        .filter_map(|input| {
81            progress.on_processing_path(&input);
82
83            let output = get_output_path(input_dir, &input, &output_dir, format);
84            let res = convert_file(&input, output, format);
85            progress.inc(1);
86
87            if matches!(res, Ok(())) {
88                progress.success_inc(1);
89                None
90            } else {
91                progress.failure_inc(1);
92                Some(input)
93            }
94        })
95        .collect();
96
97    progress.on_finish();
98
99    if err_paths.is_empty() {
100        Ok(())
101    } else {
102        FailedConvertFilesSnafu {
103            path: input_dir.to_path_buf(),
104            total_files,
105            err_paths,
106        }
107        .fail()
108    }
109}
110
111/// Convert `hkx`/`xml` file and vice versa.
112///
113/// # Note
114/// If `output` is not specified, the output is placed at the same level as `input`.
115///
116/// # Errors
117/// Failed to convert.
118pub fn convert_file<I, O>(input: I, output: Option<O>, format: OutFormat) -> Result<()>
119where
120    I: AsRef<Path>,
121    O: AsRef<Path>,
122{
123    let input = input.as_ref();
124    let in_bytes = fs::read(input)?;
125    let out_bytes = process_serde(&in_bytes, input, format)?;
126
127    Ok(write_sync(input, output, format.as_extension(), out_bytes)?)
128}