tsconfig_includes/
io.rs

1use std::{
2    error::Error,
3    fmt::Display,
4    fs::File,
5    io::{self, Read},
6    path::{Path, PathBuf},
7};
8
9use serde::Deserialize;
10
11#[derive(Debug)]
12#[non_exhaustive]
13pub struct FromFileError {
14    path: PathBuf,
15    kind: FromFileErrorKind,
16}
17
18impl Display for FromFileError {
19    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20        match &self.kind {
21            FromFileErrorKind::Open(_) => write!(f, "unable to open file {:?}", self.path),
22            FromFileErrorKind::Read(_) => write!(f, "unable to read file {:?}", self.path),
23            FromFileErrorKind::Parse(_) => write!(f, "unable to parse file {:?}", self.path),
24        }
25    }
26}
27
28impl Error for FromFileError {
29    fn source(&self) -> Option<&(dyn Error + 'static)> {
30        match &self.kind {
31            FromFileErrorKind::Open(err) => Some(err),
32            FromFileErrorKind::Read(err) => Some(err),
33            FromFileErrorKind::Parse(err) => Some(err),
34        }
35    }
36}
37
38#[derive(Debug)]
39pub enum FromFileErrorKind {
40    #[non_exhaustive]
41    Open(io::Error),
42    #[non_exhaustive]
43    Read(io::Error),
44    #[non_exhaustive]
45    Parse(serde_json::Error),
46}
47
48pub(crate) fn read_json_from_file<P, T>(path: P) -> Result<T, FromFileError>
49where
50    P: AsRef<Path>,
51    for<'de> T: Deserialize<'de>,
52{
53    fn inner<T>(path: &Path) -> Result<T, FromFileError>
54    where
55        for<'de> T: Deserialize<'de>,
56    {
57        // Reading a file into a string before invoking Serde is faster than
58        // invoking Serde from a BufReader, see
59        // https://github.com/serde-rs/json/issues/160
60        (|| {
61            let mut string = String::new();
62            File::open(path)
63                .map_err(FromFileErrorKind::Open)?
64                .read_to_string(&mut string)
65                .map_err(FromFileErrorKind::Read)?;
66            let json = serde_json::from_str(&string).map_err(FromFileErrorKind::Parse)?;
67            Ok(json)
68        })()
69        .map_err(|kind| FromFileError {
70            path: path.to_owned(),
71            kind,
72        })
73    }
74    inner(path.as_ref())
75}