1use std::io;
9use std::path::Path;
10
11use lazystr::LazyStr;
12use rewrite_macros::demomo;
13
14#[derive(Debug, thiserror::Error)]
15#[error("{msg}: {source}")]
16pub(crate) struct IOErrorContext {
17 msg: String,
18 source: std::io::Error,
19}
20
21pub fn from_err_msg(source: io::Error, msg: String) -> io::Error {
22 let kind = source.kind();
23 let error = IOErrorContext { msg, source };
24 io::Error::new(kind, error)
25}
26
27#[demomo]
28pub fn from_err_msg_path(
29 err: io::Error,
30 msg: impl AsRef<str>,
31 path: impl AsRef<Path>,
32) -> io::Error {
33 let msg = format!("{}: '{}'", msg.as_ref(), path.as_ref().display());
34 from_err_msg(err, msg)
35}
36
37pub trait IOContext<T> {
38 fn io_context(self, msg: impl LazyStr) -> io::Result<T>;
39
40 fn path_context(self, msg: impl LazyStr, path: impl AsRef<Path>) -> io::Result<T>
41 where
42 Self: Sized,
43 {
44 self.io_context(|| format!("{}: '{}'", msg.to_str(), path.as_ref().display()))
45 }
46}
47
48impl<T> IOContext<T> for std::io::Result<T> {
49 fn io_context(self, msg: impl LazyStr) -> io::Result<T> {
50 self.map_err(|err| from_err_msg(err, msg.to_str().to_string()))
51 }
52}
53
54#[cfg(test)]
55mod test {
56 use super::*;
57
58 #[test]
59 fn test_context() {
60 let res: std::io::Result<()> = Err(std::io::Error::from(std::io::ErrorKind::AlreadyExists));
61 let path: &Path = "/tmp/foo".as_ref();
62
63 let res: io::Result<()> = res.path_context("error flimflamming file", path);
64
65 let res = res.io_context(|| "flibbertigibbet".to_string());
67
68 let err = res.unwrap_err();
69 assert_eq!(
70 format!("{}", err),
71 "flibbertigibbet: error flimflamming file: '/tmp/foo': entity already exists"
72 );
73 }
74}