1use std::path::Path;
2
3use crate::pre::*;
4#[inline(always)]
6pub fn write(path: impl AsRef<Path>, content: impl AsRef<[u8]>) -> crate::Result<()> {
7 write_impl(path.as_ref(), content.as_ref())
8}
9fn write_impl(path: &Path, content: &[u8]) -> crate::Result<()> {
10 crate::trace!("write '{}'", path.display());
11 let Err(x) = std::fs::write(path, content) else {
12 return Ok(());
13 };
14 if x.kind() == std::io::ErrorKind::NotFound {
15 if let Ok(parent) = path.parent_abs() {
17 crate::trace!("retrying with parent creation: write '{}'", path.display());
18 if !parent.exists() {
19 crate::check!(
20 super::make_dir(&parent),
21 "could not automatically create parent directory for '{}'",
22 path.display()
23 )?;
24 return crate::check!(
26 std::fs::write(path, content),
27 "failed to write to file '{}'",
28 path.display()
29 );
30 }
31 }
32 }
33 make_write_error(path, x)
34}
35
36#[inline(always)]
47pub fn writer(path: impl AsRef<Path>) -> crate::Result<std::fs::File> {
48 writer_impl(path.as_ref())
49}
50fn writer_impl(path: &Path) -> crate::Result<std::fs::File> {
51 crate::trace!("writer '{}'", path.display());
52 if let Ok(file) = std::fs::File::create(path) {
53 return Ok(file);
54 }
55 write(path, [])?;
57 crate::trace!(
58 "retrying after creating the file: writer '{}'",
59 path.display()
60 );
61 match std::fs::File::create(path) {
63 Ok(file) => Ok(file),
64 Err(e) => make_write_error(path, e),
65 }
66}
67
68pub type BufFile = std::io::BufWriter<std::fs::File>;
69
70#[inline(always)]
81pub fn buf_writer(path: impl AsRef<Path>) -> crate::Result<BufFile> {
82 writer(path).map(BufFile::new)
83}
84
85#[cfg(feature = "coroutine")]
87#[inline(always)]
88pub async fn co_write(path: impl AsRef<Path>, content: impl AsRef<[u8]>) -> crate::Result<()> {
89 co_write_impl(path.as_ref(), content.as_ref()).await
90}
91#[cfg(feature = "coroutine")]
92async fn co_write_impl(path: &Path, content: &[u8]) -> crate::Result<()> {
93 crate::trace!("co_write '{}'", path.display());
94 let Err(x) = tokio::fs::write(path, content).await else {
95 return Ok(());
96 };
97 if x.kind() == std::io::ErrorKind::NotFound {
98 if let Ok(parent) = path.parent_abs() {
100 crate::trace!(
101 "retrying with parent creation: co_write '{}'",
102 path.display()
103 );
104 if !parent.exists() {
105 crate::check!(
106 super::co_make_dir(&parent).await,
107 "could not automatically create parent directory for '{}'",
108 path.display()
109 )?;
110 return crate::check!(
112 tokio::fs::write(path, content).await,
113 "failed to write to file '{}'",
114 path.display()
115 );
116 }
117 }
118 }
119 make_write_error(path, x)
120}
121
122fn make_write_error<T>(path: &Path, e: std::io::Error) -> crate::Result<T> {
123 if e.kind() == std::io::ErrorKind::NotADirectory {
124 if let Ok(parent) = path.parent_abs() {
125 if parent.exists() && !parent.is_dir() {
126 return Err(e).context(format!(
127 "the parent path '{}' exists and is not a directory",
128 path.display()
129 ))?;
130 }
131 }
132 }
133 Err(e).context(format!("failed to write to file '{}'", path.display()))
134}
135
136#[cfg(feature = "json")]
138#[inline(always)]
139pub fn write_json<S: serde::Serialize>(path: impl AsRef<Path>, content: &S) -> crate::Result<()> {
140 write_json_impl(path.as_ref(), crate::json::stringify(content))
141}
142#[cfg(feature = "json")]
144#[inline(always)]
145pub fn write_json_pretty<S: serde::Serialize>(
146 path: impl AsRef<Path>,
147 content: &S,
148) -> crate::Result<()> {
149 write_json_impl(path.as_ref(), crate::json::stringify_pretty(content))
150}
151#[cfg(feature = "json")]
152fn write_json_impl(path: &Path, content: crate::Result<String>) -> crate::Result<()> {
153 let content = crate::check!(content, "failed to write json to file '{}'", path.display())?;
154 write(path, content)
155}
156
157#[cfg(all(feature = "json", feature = "coroutine"))]
160#[inline(always)]
161pub async fn co_write_json<S: serde::Serialize>(
162 path: impl AsRef<Path>,
163 content: &S,
164) -> crate::Result<()> {
165 co_write_json_impl(path.as_ref(), crate::json::stringify(content)).await
166}
167#[cfg(all(feature = "json", feature = "coroutine"))]
170pub async fn co_write_json_pretty<S: serde::Serialize>(
171 path: impl AsRef<Path>,
172 content: &S,
173) -> crate::Result<()> {
174 co_write_json_impl(path.as_ref(), crate::json::stringify_pretty(content)).await
175}
176#[cfg(all(feature = "json", feature = "coroutine"))]
177async fn co_write_json_impl(path: &Path, content: crate::Result<String>) -> crate::Result<()> {
178 let content = crate::check!(content, "failed to write json to file '{}'", path.display())?;
179 co_write(path, content).await
180}
181
182#[cfg(feature = "toml")]
184#[inline(always)]
185pub fn write_toml<S: serde::Serialize>(path: impl AsRef<Path>, content: &S) -> crate::Result<()> {
186 write_toml_impl(path.as_ref(), crate::toml::stringify(content))
187}
188#[cfg(feature = "toml")]
190#[inline(always)]
191pub fn write_toml_pretty<S: serde::Serialize>(
192 path: impl AsRef<Path>,
193 content: &S,
194) -> crate::Result<()> {
195 write_toml_impl(path.as_ref(), crate::toml::stringify_pretty(content))
196}
197#[cfg(feature = "toml")]
198fn write_toml_impl(path: &Path, content: crate::Result<String>) -> crate::Result<()> {
199 let content = crate::check!(content, "failed to write toml to file '{}'", path.display())?;
200 write(path, content)
201}
202
203#[cfg(all(feature = "toml", feature = "coroutine"))]
206#[inline(always)]
207pub async fn co_write_toml<S: serde::Serialize>(
208 path: impl AsRef<Path>,
209 content: &S,
210) -> crate::Result<()> {
211 co_write_toml_impl(path.as_ref(), crate::toml::stringify(content)).await
212}
213#[cfg(all(feature = "toml", feature = "coroutine"))]
216pub async fn co_write_toml_pretty<S: serde::Serialize>(
217 path: impl AsRef<Path>,
218 content: &S,
219) -> crate::Result<()> {
220 co_write_toml_impl(path.as_ref(), crate::toml::stringify_pretty(content)).await
221}
222#[cfg(all(feature = "toml", feature = "coroutine"))]
223async fn co_write_toml_impl(path: &Path, content: crate::Result<String>) -> crate::Result<()> {
224 let content = crate::check!(content, "failed to write toml to file '{}'", path.display())?;
225 co_write(path, content).await
226}
227
228#[cfg(feature = "yaml")]
230#[inline(always)]
231pub fn write_yaml<S: serde::Serialize>(path: impl AsRef<Path>, content: &S) -> crate::Result<()> {
232 write_yaml_impl(path.as_ref(), crate::yaml::stringify(content))
233}
234#[cfg(feature = "yaml")]
235fn write_yaml_impl(path: &Path, content: crate::Result<String>) -> crate::Result<()> {
236 let content = crate::check!(content, "failed to write yaml to file '{}'", path.display())?;
237 write(path, content)
238}
239
240#[cfg(all(feature = "yaml", feature = "coroutine"))]
243#[inline(always)]
244pub async fn co_write_yaml<S: serde::Serialize>(
245 path: impl AsRef<Path>,
246 content: &S,
247) -> crate::Result<()> {
248 co_write_yaml_impl(path.as_ref(), crate::yaml::stringify(content)).await
249}
250#[cfg(all(feature = "yaml", feature = "coroutine"))]
251async fn co_write_yaml_impl(path: &Path, content: crate::Result<String>) -> crate::Result<()> {
252 let content = crate::check!(content, "failed to write yaml to file '{}'", path.display())?;
253 co_write(path, content).await
254}