1 use crate::errors::{Error, ErrorKind}; 2 use std::fs::{Metadata, Permissions}; 3 use std::io; 4 use std::io::{IoSlice, SeekFrom}; 5 use std::path::{Path, PathBuf}; 6 use std::pin::Pin; 7 use std::task::{ready, Context, Poll}; 8 use tokio::fs; 9 use tokio::fs::File as TokioFile; 10 use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf}; 11 12 /// Wrapper around [`tokio::fs::File`] which adds more helpful 13 /// information to all errors. 14 #[derive(Debug)] 15 #[cfg_attr(docsrs, doc(cfg(feature = "tokio")))] 16 pub struct File { 17 tokio: fs::File, 18 path: PathBuf, 19 } 20 21 impl File { 22 /// Attempts to open a file in read-only mode. 23 /// 24 /// Wrapper for [`tokio::fs::File::open`]. open(path: impl Into<PathBuf>) -> io::Result<File>25 pub async fn open(path: impl Into<PathBuf>) -> io::Result<File> { 26 let path = path.into(); 27 let f = TokioFile::open(&path) 28 .await 29 .map_err(|err| Error::build(err, ErrorKind::OpenFile, &path))?; 30 Ok(File::from_parts(f, path)) 31 } 32 33 /// Opens a file in write-only mode. 34 /// 35 /// Wrapper for [`tokio::fs::File::create`]. create(path: impl Into<PathBuf>) -> io::Result<File>36 pub async fn create(path: impl Into<PathBuf>) -> io::Result<File> { 37 let path = path.into(); 38 match TokioFile::create(&path).await { 39 Ok(f) => Ok(File::from_parts(f, path)), 40 Err(err) => Err(Error::build(err, ErrorKind::CreateFile, &path)), 41 } 42 } 43 44 /// Converts a [`crate::File`] to a [`tokio::fs::File`]. 45 /// 46 /// Wrapper for [`tokio::fs::File::from_std`]. from_std(std: crate::File) -> File47 pub fn from_std(std: crate::File) -> File { 48 let (std, path) = std.into_parts(); 49 File::from_parts(TokioFile::from_std(std), path) 50 } 51 52 /// Attempts to sync all OS-internal metadata to disk. 53 /// 54 /// Wrapper for [`tokio::fs::File::sync_all`]. sync_all(&self) -> io::Result<()>55 pub async fn sync_all(&self) -> io::Result<()> { 56 self.tokio 57 .sync_all() 58 .await 59 .map_err(|err| self.error(err, ErrorKind::SyncFile)) 60 } 61 62 /// This function is similar to `sync_all`, except that it may not 63 /// synchronize file metadata to the filesystem. 64 /// 65 /// Wrapper for [`tokio::fs::File::sync_data`]. sync_data(&self) -> io::Result<()>66 pub async fn sync_data(&self) -> io::Result<()> { 67 self.tokio 68 .sync_data() 69 .await 70 .map_err(|err| self.error(err, ErrorKind::SyncFile)) 71 } 72 73 /// Truncates or extends the underlying file, updating the size of this file to become size. 74 /// 75 /// Wrapper for [`tokio::fs::File::set_len`]. set_len(&self, size: u64) -> io::Result<()>76 pub async fn set_len(&self, size: u64) -> io::Result<()> { 77 self.tokio 78 .set_len(size) 79 .await 80 .map_err(|err| self.error(err, ErrorKind::SetLen)) 81 } 82 83 /// Queries metadata about the underlying file. 84 /// 85 /// Wrapper for [`tokio::fs::File::metadata`]. metadata(&self) -> io::Result<Metadata>86 pub async fn metadata(&self) -> io::Result<Metadata> { 87 self.tokio 88 .metadata() 89 .await 90 .map_err(|err| self.error(err, ErrorKind::Metadata)) 91 } 92 93 /// Creates a new `File` instance that shares the same underlying file handle 94 /// as the existing `File` instance. Reads, writes, and seeks will affect both 95 /// `File` instances simultaneously. 96 /// 97 /// Wrapper for [`tokio::fs::File::try_clone`]. try_clone(&self) -> io::Result<File>98 pub async fn try_clone(&self) -> io::Result<File> { 99 match self.tokio.try_clone().await { 100 Ok(file) => Ok(File::from_parts(file, self.path.clone())), 101 Err(err) => Err(self.error(err, ErrorKind::Clone)), 102 } 103 } 104 105 /// Destructures `File` into a [`crate::File`]. This function is async to allow any 106 /// in-flight operations to complete. 107 /// 108 /// Wrapper for [`tokio::fs::File::into_std`]. into_std(self) -> crate::File109 pub async fn into_std(self) -> crate::File { 110 crate::File::from_parts(self.tokio.into_std().await, self.path) 111 } 112 113 /// Tries to immediately destructure `File` into a [`crate::File`]. 114 /// 115 /// Wrapper for [`tokio::fs::File::try_into_std`]. try_into_std(self) -> Result<crate::File, File>116 pub fn try_into_std(self) -> Result<crate::File, File> { 117 match self.tokio.try_into_std() { 118 Ok(f) => Ok(crate::File::from_parts(f, self.path)), 119 Err(f) => Err(File::from_parts(f, self.path)), 120 } 121 } 122 123 /// Changes the permissions on the underlying file. 124 /// 125 /// Wrapper for [`tokio::fs::File::set_permissions`]. set_permissions(&self, perm: Permissions) -> io::Result<()>126 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { 127 self.tokio 128 .set_permissions(perm) 129 .await 130 .map_err(|err| self.error(err, ErrorKind::SetPermissions)) 131 } 132 } 133 134 /// Methods added by fs-err that are not available on 135 /// [`tokio::fs::File`]. 136 impl File { 137 /// Creates a [`File`](struct.File.html) from a raw file and its path. from_parts<P>(file: TokioFile, path: P) -> Self where P: Into<PathBuf>,138 pub fn from_parts<P>(file: TokioFile, path: P) -> Self 139 where 140 P: Into<PathBuf>, 141 { 142 File { 143 tokio: file, 144 path: path.into(), 145 } 146 } 147 148 /// Extract the raw file and its path from this [`File`](struct.File.html). into_parts(self) -> (TokioFile, PathBuf)149 pub fn into_parts(self) -> (TokioFile, PathBuf) { 150 (self.tokio, self.path) 151 } 152 153 /// Returns a reference to the underlying [`tokio::fs::File`]. file(&self) -> &TokioFile154 pub fn file(&self) -> &TokioFile { 155 &self.tokio 156 } 157 158 /// Returns a mutable reference to the underlying [`tokio::fs::File`]. file_mut(&mut self) -> &mut TokioFile159 pub fn file_mut(&mut self) -> &mut TokioFile { 160 &mut self.tokio 161 } 162 163 /// Returns a reference to the path that this file was created with. path(&self) -> &Path164 pub fn path(&self) -> &Path { 165 &self.path 166 } 167 168 /// Wrap the error in information specific to this `File` object. error(&self, source: io::Error, kind: ErrorKind) -> io::Error169 fn error(&self, source: io::Error, kind: ErrorKind) -> io::Error { 170 Error::build(source, kind, &self.path) 171 } 172 } 173 174 impl From<crate::File> for File { from(f: crate::File) -> Self175 fn from(f: crate::File) -> Self { 176 let (f, path) = f.into_parts(); 177 File::from_parts(f.into(), path) 178 } 179 } 180 181 impl From<File> for TokioFile { from(f: File) -> Self182 fn from(f: File) -> Self { 183 f.into_parts().0 184 } 185 } 186 187 #[cfg(unix)] 188 impl std::os::unix::io::AsRawFd for File { as_raw_fd(&self) -> std::os::unix::io::RawFd189 fn as_raw_fd(&self) -> std::os::unix::io::RawFd { 190 self.tokio.as_raw_fd() 191 } 192 } 193 194 #[cfg(windows)] 195 impl std::os::windows::io::AsRawHandle for File { as_raw_handle(&self) -> std::os::windows::io::RawHandle196 fn as_raw_handle(&self) -> std::os::windows::io::RawHandle { 197 self.tokio.as_raw_handle() 198 } 199 } 200 201 impl AsyncRead for File { poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>202 fn poll_read( 203 mut self: Pin<&mut Self>, 204 cx: &mut Context<'_>, 205 buf: &mut ReadBuf<'_>, 206 ) -> Poll<io::Result<()>> { 207 Poll::Ready( 208 ready!(Pin::new(&mut self.tokio).poll_read(cx, buf)) 209 .map_err(|err| self.error(err, ErrorKind::Read)), 210 ) 211 } 212 } 213 214 impl AsyncSeek for File { start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()>215 fn start_seek(mut self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> { 216 Pin::new(&mut self.tokio) 217 .start_seek(position) 218 .map_err(|err| self.error(err, ErrorKind::Seek)) 219 } 220 poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>>221 fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> { 222 Poll::Ready( 223 ready!(Pin::new(&mut self.tokio).poll_complete(cx)) 224 .map_err(|err| self.error(err, ErrorKind::Seek)), 225 ) 226 } 227 } 228 229 impl AsyncWrite for File { poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<io::Result<usize>>230 fn poll_write( 231 mut self: Pin<&mut Self>, 232 cx: &mut Context<'_>, 233 buf: &[u8], 234 ) -> Poll<io::Result<usize>> { 235 Poll::Ready( 236 ready!(Pin::new(&mut self.tokio).poll_write(cx, buf)) 237 .map_err(|err| self.error(err, ErrorKind::Write)), 238 ) 239 } 240 poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>241 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { 242 Poll::Ready( 243 ready!(Pin::new(&mut self.tokio).poll_flush(cx)) 244 .map_err(|err| self.error(err, ErrorKind::Flush)), 245 ) 246 } 247 poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>248 fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { 249 Poll::Ready( 250 ready!(Pin::new(&mut self.tokio).poll_shutdown(cx)) 251 .map_err(|err| self.error(err, ErrorKind::Flush)), 252 ) 253 } 254 poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll<io::Result<usize>>255 fn poll_write_vectored( 256 mut self: Pin<&mut Self>, 257 cx: &mut Context<'_>, 258 bufs: &[IoSlice<'_>], 259 ) -> Poll<io::Result<usize>> { 260 Poll::Ready( 261 ready!(Pin::new(&mut self.tokio).poll_write_vectored(cx, bufs)) 262 .map_err(|err| self.error(err, ErrorKind::Write)), 263 ) 264 } 265 is_write_vectored(&self) -> bool266 fn is_write_vectored(&self) -> bool { 267 self.tokio.is_write_vectored() 268 } 269 } 270