1use crate::error::{FailedReadFileSnafu, Result};
4use snafu::ResultExt as _;
5use std::borrow::Cow;
6use std::io;
7use std::{future::Future, io::Read as _, path::Path};
8use tokio::fs;
9
10pub trait ReadExt {
12 fn read_any_string(&self) -> impl Future<Output = Result<String>>;
14
15 fn read_bytes(&self) -> impl Future<Output = Result<Vec<u8>>>;
21}
22
23impl<P> ReadExt for P
24where
25 P: AsRef<Path>,
26{
27 async fn read_any_string(&self) -> Result<String> {
28 let input = self.as_ref();
29 let mut string = String::new();
30
31 let bytes = input.read_bytes().await?;
32 let mut decoder = encoding_rs_io::DecodeReaderBytes::new(bytes.as_slice());
33 decoder.read_to_string(&mut string)?;
34 Ok(string)
35 }
36
37 async fn read_bytes(&self) -> Result<Vec<u8>> {
38 let input = self.as_ref();
39 fs::read(input).await.context(FailedReadFileSnafu {
40 path: input.to_path_buf(),
41 })
42 }
43}
44
45pub async fn write<I, O>(
53 input: I,
54 output: Option<O>,
55 ext: &str,
56 contents: impl AsRef<[u8]>,
57) -> io::Result<()>
58where
59 I: AsRef<Path>,
60 O: AsRef<Path>,
61{
62 let input = input.as_ref();
63
64 let output = output.as_ref().map_or_else(
65 || {
66 let mut output = input.to_path_buf();
67 output.set_extension(ext);
68 Cow::Owned(output)
69 },
70 |output| Cow::Borrowed(output.as_ref()),
71 );
72
73 if let Some(parent) = output.parent() {
74 fs::create_dir_all(parent).await?;
75 }
76 fs::write(&output, contents).await?;
77
78 #[cfg(feature = "tracing")]
79 tracing::info!(
80 "Write Input: {} -> Output: {}",
81 input.display(),
82 output.display()
83 );
84 Ok(())
85}
86
87pub fn write_sync<I, O>(
95 input: I,
96 output: Option<O>,
97 ext: &str,
98 contents: impl AsRef<[u8]>,
99) -> io::Result<()>
100where
101 I: AsRef<Path>,
102 O: AsRef<Path>,
103{
104 let input = input.as_ref();
105
106 let output = output.as_ref().map_or_else(
107 || {
108 let mut output = input.to_path_buf();
109 output.set_extension(ext);
110 Cow::Owned(output)
111 },
112 |output| Cow::Borrowed(output.as_ref()),
113 );
114
115 if let Some(parent) = output.parent() {
116 std::fs::create_dir_all(parent)?;
117 }
118 std::fs::write(&output, contents)?;
119
120 #[cfg(feature = "tracing")]
121 tracing::info!(
122 "Write Input: {} -> Output: {}",
123 input.display(),
124 output.display()
125 );
126 Ok(())
127}