@@ -30,7 +30,7 @@ pub async fn symlink<P: AsRef<Path>, Q: AsRef<Path>>(src: P, dst: Q) -> io::Resu
30
30
}
31
31
32
32
cfg_not_docs ! {
33
- pub use std:: os:: unix:: fs:: { DirBuilderExt , DirEntryExt , OpenOptionsExt } ;
33
+ pub use std:: os:: unix:: fs:: { DirBuilderExt , DirEntryExt , OpenOptionsExt , FileExt } ;
34
34
}
35
35
36
36
cfg_docs ! {
@@ -68,4 +68,196 @@ cfg_docs! {
68
68
/// This options overwrites any previously set custom flags.
69
69
fn custom_flags( & mut self , flags: i32 ) -> & mut Self ;
70
70
}
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
+ }
71
263
}
0 commit comments