util/
errors.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8use 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        // Can wrap further with more context.
66        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}