ts_io/
read_file.rs

1use std::{
2    fs, io,
3    path::{Path, PathBuf},
4};
5
6use ts_path::DisplayPath;
7
8/// Error variants for reading a file.
9#[derive(Debug)]
10#[non_exhaustive]
11#[allow(missing_docs)]
12pub enum ReadFileError {
13    #[non_exhaustive]
14    DoesNotExist { path: PathBuf },
15
16    #[non_exhaustive]
17    NotAFile { path: PathBuf },
18
19    #[non_exhaustive]
20    ReadError { path: PathBuf, source: io::Error },
21}
22impl core::fmt::Display for ReadFileError {
23    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24        match &self {
25            Self::DoesNotExist { path, .. } => {
26                write!(f, "`{}` does not exist", path.opinionated_display())
27            }
28            Self::NotAFile { path, .. } => {
29                write!(f, "`{}` is not a file", path.opinionated_display())
30            }
31            Self::ReadError { path, .. } => {
32                write!(f, "could not read `{}`", path.opinionated_display())
33            }
34        }
35    }
36}
37impl core::error::Error for ReadFileError {
38    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
39        match &self {
40            Self::ReadError { source, .. } => Some(source),
41            _ => None,
42        }
43    }
44}
45impl ReadFileError {
46    #[allow(missing_docs)]
47    pub(crate) fn read_error(source: io::Error, path: &Path) -> Self {
48        Self::ReadError {
49            path: path.to_path_buf(),
50            source,
51        }
52    }
53}
54
55/// Read a file, returning presentable error variants.
56pub fn read_file(path: &Path) -> Result<Vec<u8>, ReadFileError> {
57    if !fs::exists(path).map_err(|source| ReadFileError::read_error(source, path))? {
58        return Err(ReadFileError::DoesNotExist {
59            path: path.to_path_buf(),
60        });
61    }
62
63    let metadata = path
64        .metadata()
65        .map_err(|source| ReadFileError::read_error(source, path))?;
66
67    if metadata.is_dir() {
68        return Err(ReadFileError::NotAFile {
69            path: path.to_path_buf(),
70        });
71    }
72
73    fs::read(path).map_err(|source| ReadFileError::read_error(source, path))
74}
75
76/// Read a file to a string, returning presentable error variants.
77pub fn read_file_to_string(path: &Path) -> Result<String, ReadFileError> {
78    if !fs::exists(path).map_err(|source| ReadFileError::read_error(source, path))? {
79        return Err(ReadFileError::DoesNotExist {
80            path: path.to_path_buf(),
81        });
82    }
83
84    let metadata = path
85        .metadata()
86        .map_err(|source| ReadFileError::read_error(source, path))?;
87
88    if metadata.is_dir() {
89        return Err(ReadFileError::NotAFile {
90            path: path.to_path_buf(),
91        });
92    }
93
94    fs::read_to_string(path).map_err(|source| ReadFileError::read_error(source, path))
95}