@@ -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