use std::{
fs::File,
io::{self, Write},
num::NonZeroUsize,
path::{Path, PathBuf},
};
use errortools::{MainResult, WithContext, with_context::WithPath};
const RETRY_ATTEMPTS: NonZeroUsize = NonZeroUsize::new(3).unwrap();
#[derive(Debug, thiserror::Error)]
enum FsError {
#[error("Failed to create file")]
Create(#[source] WithPath<PathBuf, WithContext<usize, io::Error>>),
#[error("Failed to write file")]
Write(#[source] WithPath<&'static Path, io::Error>),
}
#[derive(Debug, thiserror::Error)]
enum AppError {
#[error("An FS error happened")]
Fs(#[source] FsError),
}
fn create_with_retry(
path: &Path,
attempts: NonZeroUsize,
) -> Result<File, WithContext<usize, io::Error>> {
let last = attempts.get();
for _ in 1..last {
if let Ok(file) = File::create(path) {
return Ok(file);
}
}
File::create(path).map_err(|e| WithContext::new(last, e))
}
fn write_file(path: &'static Path, contents: &[u8]) -> Result<(), FsError> {
let mut file = create_with_retry(path, RETRY_ATTEMPTS)
.map_err(|e| FsError::Create(WithContext::new(path.to_path_buf(), e)))?;
file.write_all(contents)
.map_err(|e| WithContext::new(path, e))
.map_err(FsError::Write)?;
Ok(())
}
fn main() -> MainResult<AppError> {
write_file(Path::new("no/such/dir/output.txt"), b"hello, errortools\n")
.map_err(AppError::Fs)?;
Ok(())
}