1use std::error::Error as StdError;
2use std::fmt;
3use std::io;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Copy)]
7pub(crate) enum ErrorKind {
8 OpenFile,
9 CreateFile,
10 CreateDir,
11 SyncFile,
12 SetLen,
13 Metadata,
14 Clone,
15 SetPermissions,
16 Read,
17 Seek,
18 Write,
19 Flush,
20 ReadDir,
21 RemoveFile,
22 RemoveDir,
23 Canonicalize,
24 ReadLink,
25 SymlinkMetadata,
26 #[allow(dead_code)]
27 FileExists,
28
29 #[cfg(windows)]
30 SeekRead,
31 #[cfg(windows)]
32 SeekWrite,
33
34 #[cfg(unix)]
35 ReadAt,
36 #[cfg(unix)]
37 WriteAt,
38}
39
40/// Contains an IO error that has a file path attached.
41///
42/// This type is never returned directly, but is instead wrapped inside yet
43/// another IO error.
44#[derive(Debug)]
45pub(crate) struct Error {
46 kind: ErrorKind,
47 source: io::Error,
48 path: PathBuf,
49}
50
51impl Error {
52 pub fn build(source: io::Error, kind: ErrorKind, path: impl Into<PathBuf>) -> io::Error {
53 io::Error::new(
54 source.kind(),
55 Self {
56 kind,
57 source,
58 path: path.into(),
59 },
60 )
61 }
62}
63
64impl fmt::Display for Error {
65 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
66 use ErrorKind::*;
67
68 let path = self.path.display();
69
70 match self.kind {
71 OpenFile => write!(formatter, "failed to open file `{}`", path),
72 CreateFile => write!(formatter, "failed to create file `{}`", path),
73 CreateDir => write!(formatter, "failed to create directory `{}`", path),
74 SyncFile => write!(formatter, "failed to sync file `{}`", path),
75 SetLen => write!(formatter, "failed to set length of file `{}`", path),
76 Metadata => write!(formatter, "failed to query metadata of file `{}`", path),
77 Clone => write!(formatter, "failed to clone handle for file `{}`", path),
78 SetPermissions => write!(formatter, "failed to set permissions for file `{}`", path),
79 Read => write!(formatter, "failed to read from file `{}`", path),
80 Seek => write!(formatter, "failed to seek in file `{}`", path),
81 Write => write!(formatter, "failed to write to file `{}`", path),
82 Flush => write!(formatter, "failed to flush file `{}`", path),
83 ReadDir => write!(formatter, "failed to read directory `{}`", path),
84 RemoveFile => write!(formatter, "failed to remove file `{}`", path),
85 RemoveDir => write!(formatter, "failed to remove directory `{}`", path),
86 Canonicalize => write!(formatter, "failed to canonicalize path `{}`", path),
87 ReadLink => write!(formatter, "failed to read symbolic link `{}`", path),
88 SymlinkMetadata => write!(formatter, "failed to query metadata of symlink `{}`", path),
89 FileExists => write!(formatter, "failed to check file existance `{}`", path),
90
91 #[cfg(windows)]
92 SeekRead => write!(formatter, "failed to seek and read from `{}`", path),
93 #[cfg(windows)]
94 SeekWrite => write!(formatter, "failed to seek and write to `{}`", path),
95
96 #[cfg(unix)]
97 ReadAt => write!(formatter, "failed to read with offset from `{}`", path),
98 #[cfg(unix)]
99 WriteAt => write!(formatter, "failed to write with offset to `{}`", path),
100 }
101 }
102}
103
104impl StdError for Error {
105 fn cause(&self) -> Option<&dyn StdError> {
106 self.source()
107 }
108
109 fn source(&self) -> Option<&(dyn StdError + 'static)> {
110 Some(&self.source)
111 }
112}
113
114#[derive(Debug, Clone, Copy)]
115pub(crate) enum SourceDestErrorKind {
116 Copy,
117 HardLink,
118 Rename,
119 SoftLink,
120
121 #[cfg(unix)]
122 Symlink,
123
124 #[cfg(windows)]
125 SymlinkDir,
126 #[cfg(windows)]
127 SymlinkFile,
128}
129
130/// Error type used by functions like `fs::copy` that holds two paths.
131#[derive(Debug)]
132pub(crate) struct SourceDestError {
133 kind: SourceDestErrorKind,
134 source: io::Error,
135 from_path: PathBuf,
136 to_path: PathBuf,
137}
138
139impl SourceDestError {
140 pub fn build(
141 source: io::Error,
142 kind: SourceDestErrorKind,
143 from_path: impl Into<PathBuf>,
144 to_path: impl Into<PathBuf>,
145 ) -> io::Error {
146 io::Error::new(
147 source.kind(),
148 Self {
149 kind,
150 source,
151 from_path: from_path.into(),
152 to_path: to_path.into(),
153 },
154 )
155 }
156}
157
158impl fmt::Display for SourceDestError {
159 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
160 let from = self.from_path.display();
161 let to = self.to_path.display();
162 match self.kind {
163 SourceDestErrorKind::Copy => {
164 write!(formatter, "failed to copy file from {} to {}", from, to)
165 }
166 SourceDestErrorKind::HardLink => {
167 write!(formatter, "failed to hardlink file from {} to {}", from, to)
168 }
169 SourceDestErrorKind::Rename => {
170 write!(formatter, "failed to rename file from {} to {}", from, to)
171 }
172 SourceDestErrorKind::SoftLink => {
173 write!(formatter, "failed to softlink file from {} to {}", from, to)
174 }
175
176 #[cfg(unix)]
177 SourceDestErrorKind::Symlink => {
178 write!(formatter, "failed to symlink file from {} to {}", from, to)
179 }
180
181 #[cfg(windows)]
182 SourceDestErrorKind::SymlinkFile => {
183 write!(formatter, "failed to symlink file from {} to {}", from, to)
184 }
185 #[cfg(windows)]
186 SourceDestErrorKind::SymlinkDir => {
187 write!(formatter, "failed to symlink dir from {} to {}", from, to)
188 }
189 }
190 }
191}
192
193impl StdError for SourceDestError {
194 fn cause(&self) -> Option<&dyn StdError> {
195 self.source()
196 }
197
198 fn source(&self) -> Option<&(dyn StdError + 'static)> {
199 Some(&self.source)
200 }
201}
202