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