@@ -30,10 +30,12 @@ pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Resu
3030}
3131
3232cfg_not_docs! {
33- pub use std::os::unix::fs::{DirBuilderExt, DirEntryExt, OpenOptionsExt};
33+ pub use std::os::unix::fs::{DirBuilderExt, DirEntryExt, OpenOptionsExt, FileExt };
3434}
3535
3636cfg_docs! {
37+ use async_trait::async_trait;
38+
3739 /// Unix-specific extensions to `DirBuilder`.
3840 pub trait DirBuilderExt {
3941 /// Sets the mode to create new directories with. This option defaults to
@@ -68,4 +70,196 @@ cfg_docs! {
6870 /// This options overwrites any previously set custom flags.
6971 fn custom_flags(&mut self, flags: i32) -> &mut Self;
7072 }
73+
74+ /// Unix-specific extensions to [`fs::File`].
75+ #[async_trait]
76+ pub trait FileExt {
77+ /// Reads a number of bytes starting from a given offset.
78+ ///
79+ /// Returns the number of bytes read.
80+ ///
81+ /// The offset is relative to the start of the file and thus independent
82+ /// from the current cursor.
83+ ///
84+ /// The current file cursor is not affected by this function.
85+ ///
86+ /// Note that similar to [`File::read`], it is not an error to return with a
87+ /// short read.
88+ ///
89+ /// [`File::read`]: fs::File::read
90+ ///
91+ /// # Examples
92+ ///
93+ /// ```no_run
94+ /// use async_std::io;
95+ /// use async_std::fs::File;
96+ /// use async_std::os::unix::prelude::FileExt;
97+ ///
98+ /// async fn main() -> io::Result<()> {
99+ /// let mut buf = [0u8; 8];
100+ /// let file = File::open("foo.txt").await?;
101+ ///
102+ /// // We now read 8 bytes from the offset 10.
103+ /// let num_bytes_read = file.read_at(&mut buf, 10).await?;
104+ /// println!("read {} bytes: {:?}", num_bytes_read, buf);
105+ /// Ok(())
106+ /// }
107+ /// ```
108+ async fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;
109+
110+ /// Reads the exact number of byte required to fill `buf` from the given offset.
111+ ///
112+ /// The offset is relative to the start of the file and thus independent
113+ /// from the current cursor.
114+ ///
115+ /// The current file cursor is not affected by this function.
116+ ///
117+ /// Similar to [`io::Read::read_exact`] but uses [`read_at`] instead of `read`.
118+ ///
119+ /// [`read_at`]: FileExt::read_at
120+ ///
121+ /// # Errors
122+ ///
123+ /// If this function encounters an error of the kind
124+ /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation
125+ /// will continue.
126+ ///
127+ /// If this function encounters an "end of file" before completely filling
128+ /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`].
129+ /// The contents of `buf` are unspecified in this case.
130+ ///
131+ /// If any other read error is encountered then this function immediately
132+ /// returns. The contents of `buf` are unspecified in this case.
133+ ///
134+ /// If this function returns an error, it is unspecified how many bytes it
135+ /// has read, but it will never read more than would be necessary to
136+ /// completely fill the buffer.
137+ ///
138+ /// # Examples
139+ ///
140+ /// ```no_run
141+ /// use async_std::io;
142+ /// use async_std::fs::File;
143+ /// use async_std::os::unix::prelude::FileExt;
144+ ///
145+ /// async fn main() -> io::Result<()> {
146+ /// let mut buf = [0u8; 8];
147+ /// let file = File::open("foo.txt").await?;
148+ ///
149+ /// // We now read exactly 8 bytes from the offset 10.
150+ /// file.read_exact_at(&mut buf, 10).await?;
151+ /// println!("read {} bytes: {:?}", buf.len(), buf);
152+ /// Ok(())
153+ /// }
154+ /// ```
155+ async fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
156+ while !buf.is_empty() {
157+ match self.read_at(buf, offset).await {
158+ Ok(0) => break,
159+ Ok(n) => {
160+ let tmp = buf;
161+ buf = &mut tmp[n..];
162+ offset += n as u64;
163+ }
164+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
165+ Err(e) => return Err(e),
166+ }
167+ }
168+ if !buf.is_empty() {
169+ Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
170+ } else {
171+ Ok(())
172+ }
173+ }
174+
175+ /// Writes a number of bytes starting from a given offset.
176+ ///
177+ /// Returns the number of bytes written.
178+ ///
179+ /// The offset is relative to the start of the file and thus independent
180+ /// from the current cursor.
181+ ///
182+ /// The current file cursor is not affected by this function.
183+ ///
184+ /// When writing beyond the end of the file, the file is appropriately
185+ /// extended and the intermediate bytes are initialized with the value 0.
186+ ///
187+ /// Note that similar to [`File::write`], it is not an error to return a
188+ /// short write.
189+ ///
190+ /// [`File::write`]: fs::File::write
191+ ///
192+ /// # Examples
193+ ///
194+ /// ```no_run
195+ /// use async_std::fs::File;
196+ /// use async_std::io;
197+ /// use async_std::os::unix::prelude::FileExt;
198+ ///
199+ /// async fn main() -> io::Result<()> {
200+ /// let file = File::open("foo.txt").await?;
201+ ///
202+ /// // We now write at the offset 10.
203+ /// file.write_at(b"sushi", 10).await?;
204+ /// Ok(())
205+ /// }
206+ /// ```
207+ async fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
208+
209+ /// Attempts to write an entire buffer starting from a given offset.
210+ ///
211+ /// The offset is relative to the start of the file and thus independent
212+ /// from the current cursor.
213+ ///
214+ /// The current file cursor is not affected by this function.
215+ ///
216+ /// This method will continuously call [`write_at`] until there is no more data
217+ /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is
218+ /// returned. This method will not return until the entire buffer has been
219+ /// successfully written or such an error occurs. The first error that is
220+ /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be
221+ /// returned.
222+ ///
223+ /// # Errors
224+ ///
225+ /// This function will return the first error of
226+ /// non-[`io::ErrorKind::Interrupted`] kind that [`write_at`] returns.
227+ ///
228+ /// [`write_at`]: FileExt::write_at
229+ ///
230+ /// # Examples
231+ ///
232+ /// ```no_run
233+ /// use async_std::fs::File;
234+ /// use async_std::io;
235+ /// use async_std::os::unix::prelude::FileExt;
236+ ///
237+ /// async fn main() -> io::Result<()> {
238+ /// let file = File::open("foo.txt").await?;
239+ ///
240+ /// // We now write at the offset 10.
241+ /// file.write_all_at(b"sushi", 10).await?;
242+ /// Ok(())
243+ /// }
244+ /// ```
245+ async fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
246+ while !buf.is_empty() {
247+ match self.write_at(buf, offset).await {
248+ Ok(0) => {
249+ return Err(io::Error::new(
250+ io::ErrorKind::WriteZero,
251+ "failed to write whole buffer",
252+ ));
253+ }
254+ Ok(n) => {
255+ buf = &buf[n..];
256+ offset += n as u64
257+ }
258+ Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
259+ Err(e) => return Err(e),
260+ }
261+ }
262+ Ok(())
263+ }
264+ }
71265}
0 commit comments