serde_hkx_features/convert/
tokio.rs

1//! Async Convert hkx <-> xml
2use super::{OutFormat, get_output_path, get_supported_files, process_serde};
3use crate::{
4    error::Result,
5    fs::{ReadExt as _, write},
6    progress::{DefaultProgressMonitor, ProgressHandler},
7};
8use std::{
9    io::{self},
10    path::{Path, PathBuf},
11};
12
13/// Convert dir or file(hkx, xml).
14///
15/// # Note
16/// If `output` is not specified, the output is placed at the same level as `input`.
17///
18/// # Errors
19/// Failed to convert.
20pub async fn convert<I, O>(input: I, output: Option<O>, format: OutFormat) -> Result<()>
21where
22    I: AsRef<Path>,
23    O: AsRef<Path>,
24{
25    convert_progress(input, output, format, DefaultProgressMonitor).await
26}
27
28/// Convert dir or file(hkx, xml).
29///
30/// # Note
31/// If `output` is not specified, the output is placed at the same level as `input`.
32///
33/// # Errors
34/// Failed to convert.
35pub async fn convert_progress<I, O, P>(
36    input: I,
37    output: Option<O>,
38    format: OutFormat,
39    progress: P,
40) -> Result<()>
41where
42    I: AsRef<Path>,
43    O: AsRef<Path>,
44    P: ProgressHandler + Send + Clone + 'static,
45{
46    let input = input.as_ref();
47    if input.is_dir() {
48        convert_dir(input, output, format, progress).await?;
49    } else if input.is_file() {
50        convert_file(input, output, format).await?;
51    } else {
52        return Err(io::Error::new(
53            io::ErrorKind::NotFound,
54            format!("The path does not exist: {}", input.display()),
55        ))?;
56    }
57
58    Ok(())
59}
60
61/// Convert dir.
62///
63/// # Note
64/// If `output` is not specified, the output is placed at the same level as `input`.
65///
66/// # Errors
67/// Failed to convert.
68pub async fn convert_dir<I, O, P>(
69    input_dir: I,
70    output_dir: Option<O>,
71    format: OutFormat,
72    mut progress: P,
73) -> Result<()>
74where
75    I: AsRef<Path>,
76    O: AsRef<Path>,
77    P: ProgressHandler + Send + Clone + 'static,
78{
79    let mut task_handles: Vec<tokio::task::JoinHandle<Result<()>>> = Vec::new();
80
81    let input_dir = input_dir.as_ref();
82    let files: Vec<PathBuf> = get_supported_files(input_dir);
83
84    if files.is_empty() {
85        progress.on_empty();
86        return Ok(());
87    }
88
89    progress.on_set_total(files.len());
90
91    for input in files {
92        // Convert only if there is a supported extension.
93        progress.on_processing_path(&input);
94
95        // If `out_dir` is specified, join the internal dirs of `input_dir` with it as root.
96        let output = get_output_path(input_dir, &input, &output_dir, format);
97
98        let progress = progress.clone();
99        task_handles.push(tokio::spawn(async move {
100            match convert_file(&input, output, format).await {
101                Ok(_) => {
102                    progress.success_inc(1);
103                    Ok(())
104                }
105                Err(err) => {
106                    progress.failure_inc(1);
107                    #[cfg(feature = "tracing")]
108                    tracing::error!("Failed to convert: {}", input.display());
109                    Err(err)
110                }
111            }?;
112            progress.inc(1);
113            Result::Ok(())
114        }));
115    }
116
117    for task_handle in task_handles {
118        task_handle.await??;
119    }
120
121    progress.on_finish();
122    Ok(())
123}
124
125/// Convert `hkx`/`xml` file and vice vasa.
126///
127/// # Note
128/// If `output` is not specified, the output is placed at the same level as `input`.
129///
130/// # Errors
131/// Failed to convert.
132pub async fn convert_file<I, O>(input: I, output: Option<O>, format: OutFormat) -> Result<()>
133where
134    I: AsRef<Path>,
135    O: AsRef<Path>,
136{
137    let input = input.as_ref();
138    let in_bytes = input.read_bytes().await?;
139    let out_bytes = process_serde(&in_bytes, input, format)?;
140
141    Ok(write(input, output, format.as_extension(), out_bytes).await?)
142}