1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
use std::{
    error::Error,
    fmt::Display,
    fs::File,
    io::{self, Read},
    path::{Path, PathBuf},
};

use serde::Deserialize;

#[derive(Debug)]
#[non_exhaustive]
pub struct FromFileError {
    path: PathBuf,
    kind: FromFileErrorKind,
}

impl Display for FromFileError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match &self.kind {
            FromFileErrorKind::Open(_) => write!(f, "unable to open file {:?}", self.path),
            FromFileErrorKind::Read(_) => write!(f, "unable to read file {:?}", self.path),
            FromFileErrorKind::Parse(_) => write!(f, "unable to parse file {:?}", self.path),
        }
    }
}

impl Error for FromFileError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.kind {
            FromFileErrorKind::Open(err) => Some(err),
            FromFileErrorKind::Read(err) => Some(err),
            FromFileErrorKind::Parse(err) => Some(err),
        }
    }
}

#[derive(Debug)]
pub enum FromFileErrorKind {
    #[non_exhaustive]
    Open(io::Error),
    #[non_exhaustive]
    Read(io::Error),
    #[non_exhaustive]
    Parse(serde_json::Error),
}

pub(crate) fn read_json_from_file<P, T>(path: P) -> Result<T, FromFileError>
where
    P: AsRef<Path>,
    for<'de> T: Deserialize<'de>,
{
    fn inner<T>(path: &Path) -> Result<T, FromFileError>
    where
        for<'de> T: Deserialize<'de>,
    {
        // Reading a file into a string before invoking Serde is faster than
        // invoking Serde from a BufReader, see
        // https://github.com/serde-rs/json/issues/160
        (|| {
            let mut string = String::new();
            File::open(path)
                .map_err(FromFileErrorKind::Open)?
                .read_to_string(&mut string)
                .map_err(FromFileErrorKind::Read)?;
            let json = serde_json::from_str(&string).map_err(FromFileErrorKind::Parse)?;
            Ok(json)
        })()
        .map_err(|kind| FromFileError {
            path: path.to_owned(),
            kind,
        })
    }
    inner(path.as_ref())
}