@@ -30,10 +30,12 @@ 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 ! {
37
+ use async_trait:: async_trait;
38
+
37
39
/// Unix-specific extensions to `DirBuilder`.
38
40
pub trait DirBuilderExt {
39
41
/// Sets the mode to create new directories with. This option defaults to
@@ -68,4 +70,196 @@ cfg_docs! {
68
70
/// This options overwrites any previously set custom flags.
69
71
fn custom_flags( & mut self , flags: i32 ) -> & mut Self ;
70
72
}
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
+ }
71
265
}
0 commit comments