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