1use std::path::Path;
5
6use super::OpenOptions;
7use crate::{maybe_fut_constructor_result, maybe_fut_method};
8
9#[derive(Debug, Read, Seek, Write, Unwrap)]
10#[io(feature("tokio-fs"))]
11#[unwrap_types(std(std::fs::File), tokio(tokio::fs::File), tokio_gated("tokio-fs"))]
12pub struct File(FileInner);
14
15#[derive(Debug)]
17enum FileInner {
18 Std(std::fs::File),
20 #[cfg(tokio_fs)]
21 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-fs")))]
22 Tokio(tokio::fs::File),
24}
25
26impl From<std::fs::File> for File {
27 fn from(file: std::fs::File) -> Self {
28 Self(FileInner::Std(file))
29 }
30}
31
32#[cfg(tokio_fs)]
33#[cfg_attr(docsrs, doc(cfg(feature = "tokio-fs")))]
34impl From<tokio::fs::File> for File {
35 fn from(file: tokio::fs::File) -> Self {
36 Self(FileInner::Tokio(file))
37 }
38}
39
40impl File {
41 maybe_fut_constructor_result!(
42 open(path: impl AsRef<Path>) -> std::io::Result<Self>,
52 std::fs::File::open,
53 tokio::fs::File::open,
54 tokio_fs
55 );
56
57 maybe_fut_constructor_result!(
58 create(path: impl AsRef<Path>) -> std::io::Result<Self>,
68 std::fs::File::create,
69 tokio::fs::File::create,
70 tokio_fs
71 );
72
73 maybe_fut_constructor_result!(
74 create_new(path: impl AsRef<Path>) -> std::io::Result<Self>,
89 std::fs::File::create_new,
90 tokio::fs::File::create_new,
91 tokio_fs
92 );
93
94 maybe_fut_method!(
95 metadata() -> std::io::Result<std::fs::Metadata>,
97 FileInner::Std,
98 FileInner::Tokio,
99 tokio_fs
100 );
101
102 #[inline]
111 pub fn open_options() -> OpenOptions {
112 OpenOptions::new()
113 }
114
115 maybe_fut_method!(
116 set_len(size: u64) -> std::io::Result<()>,
125 FileInner::Std,
126 FileInner::Tokio,
127 tokio_fs
128 );
129
130 maybe_fut_method!(
131 set_permissions(perm: std::fs::Permissions) -> std::io::Result<()>,
140 FileInner::Std,
141 FileInner::Tokio,
142 tokio_fs
143 );
144
145 maybe_fut_method!(
146 sync_all() -> std::io::Result<()>,
150 FileInner::Std,
151 FileInner::Tokio,
152 tokio_fs
153 );
154
155 maybe_fut_method!(
156 sync_data() -> std::io::Result<()>,
163 FileInner::Std,
164 FileInner::Tokio,
165 tokio_fs
166 );
167
168 pub async fn try_clone(&self) -> std::io::Result<Self> {
171 match &self.0 {
172 FileInner::Std(file) => file.try_clone().map(Self::from),
173 #[cfg(tokio_fs)]
174 FileInner::Tokio(file) => file.try_clone().await.map(Self::from),
175 }
176 }
177 pub async fn to_std(self) -> std::fs::File {
181 match self.0 {
182 FileInner::Std(file) => file,
183 #[cfg(tokio_fs)]
184 FileInner::Tokio(file) => file.into_std().await,
185 }
186 }
187
188 #[cfg(tokio_fs)]
192 #[cfg_attr(docsrs, doc(cfg(feature = "tokio-fs")))]
193 pub async fn to_tokio(self) -> tokio::fs::File {
194 match self.0 {
195 FileInner::Std(file) => tokio::fs::File::from_std(file),
196 FileInner::Tokio(file) => file,
197 }
198 }
199}
200
201#[cfg(unix)]
202impl std::os::fd::AsFd for File {
203 fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
204 match &self.0 {
205 FileInner::Std(file) => file.as_fd(),
206 #[cfg(tokio_fs)]
207 FileInner::Tokio(file) => file.as_fd(),
208 }
209 }
210}
211
212#[cfg(windows)]
213impl std::os::windows::io::AsHandle for File {
214 fn as_handle(&self) -> std::os::windows::io::BorrowedHandle<'_> {
215 match &self.0 {
216 FileInner::Std(file) => file.as_handle(),
217 #[cfg(tokio_fs)]
218 FileInner::Tokio(file) => file.as_handle(),
219 }
220 }
221}
222
223#[cfg(unix)]
224impl std::os::fd::AsRawFd for File {
225 fn as_raw_fd(&self) -> std::os::fd::RawFd {
226 match &self.0 {
227 FileInner::Std(file) => file.as_raw_fd(),
228 #[cfg(tokio_fs)]
229 FileInner::Tokio(file) => file.as_raw_fd(),
230 }
231 }
232}
233
234#[cfg(windows)]
235impl std::os::windows::io::AsRawHandle for File {
236 fn as_raw_handle(&self) -> std::os::windows::io::RawHandle {
237 match &self.0 {
238 FileInner::Std(file) => file.as_raw_handle(),
239 #[cfg(tokio_fs)]
240 FileInner::Tokio(file) => file.as_raw_handle(),
241 }
242 }
243}
244
245#[cfg(unix)]
246impl std::os::fd::FromRawFd for File {
247 unsafe fn from_raw_fd(fd: std::os::fd::RawFd) -> Self {
248 #[cfg(tokio_fs)]
249 {
250 if crate::context::is_async_context() {
251 Self(FileInner::Tokio(unsafe {
252 tokio::fs::File::from_raw_fd(fd)
253 }))
254 } else {
255 Self(FileInner::Std(unsafe { std::fs::File::from_raw_fd(fd) }))
256 }
257 }
258 #[cfg(not(tokio_fs))]
259 {
260 Self(FileInner::Std(unsafe { std::fs::File::from_raw_fd(fd) }))
261 }
262 }
263}
264
265#[cfg(windows)]
266impl std::os::windows::io::FromRawHandle for File {
267 unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
268 #[cfg(tokio_fs)]
269 {
270 if crate::context::is_async_context() {
271 Self(FileInner::Tokio(unsafe {
272 tokio::fs::File::from_raw_handle(handle)
273 }))
274 } else {
275 Self(FileInner::Std(unsafe {
276 std::fs::File::from_raw_handle(handle)
277 }))
278 }
279 }
280 #[cfg(not(tokio_fs))]
281 {
282 Self(FileInner::Std(unsafe {
283 std::fs::File::from_raw_handle(handle)
284 }))
285 }
286 }
287}
288
289#[cfg(test)]
290mod test {
291
292 use tempfile::NamedTempFile;
293
294 use super::*;
295 use crate::SyncRuntime;
296 use crate::io::{Read, Seek, Write};
297
298 #[test]
299 fn test_should_instantiate_file_sync() {
300 let temp = NamedTempFile::new().expect("Failed to create temp file");
301
302 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
304
305 let variant = SyncRuntime::block_on(File::open(temp.path())).expect("Failed to open file");
306 assert!(matches!(variant.0, FileInner::Std(_)));
307 }
308
309 #[tokio::test]
310 async fn test_should_instantiate_file_async() {
311 let temp = NamedTempFile::new().expect("Failed to create temp file");
312
313 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
315
316 let variant = File::open(temp.path()).await.expect("Failed to open file");
317 assert!(matches!(variant.0, FileInner::Tokio(_)));
318 }
319
320 #[test]
321 fn test_should_create_file_sync() {
322 let temp = NamedTempFile::new().expect("Failed to create temp file");
323
324 let variant =
325 SyncRuntime::block_on(File::create(temp.path())).expect("Failed to open file");
326 assert!(matches!(variant.0, FileInner::Std(_)));
327 }
328
329 #[tokio::test]
330 async fn test_should_create_file_async() {
331 let temp = NamedTempFile::new().expect("Failed to create temp file");
332
333 let variant = File::create(temp.path())
334 .await
335 .expect("Failed to open file");
336 assert!(matches!(variant.0, FileInner::Tokio(_)));
337 }
338
339 #[test]
340 fn test_should_get_metadata_sync() {
341 let temp = NamedTempFile::new().expect("Failed to create temp file");
342
343 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
345
346 let file = SyncRuntime::block_on(File::open(temp.path())).expect("Failed to open file");
347 SyncRuntime::block_on(file.metadata()).expect("Failed to get metadata");
348 }
349
350 #[tokio::test]
351 async fn test_should_get_metadata_async() {
352 let temp = NamedTempFile::new().expect("Failed to create temp file");
353
354 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
356
357 File::open(temp.path())
358 .await
359 .expect("Failed to open file")
360 .metadata()
361 .await
362 .expect("Failed to get metadata");
363 }
364
365 #[test]
366 fn test_should_convert_to_std() {
367 let temp = NamedTempFile::new().expect("Failed to create temp file");
368
369 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
371
372 let file = SyncRuntime::block_on(File::open(temp.path())).expect("Failed to open file");
373 let _std_file = SyncRuntime::block_on(file.to_std());
374 }
375
376 #[tokio::test]
377 async fn test_should_convert_to_tokio() {
378 let temp = NamedTempFile::new().expect("Failed to create temp file");
379
380 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
382
383 let file = File::open(temp.path()).await.expect("Failed to open file");
384 let _tokio_file = file.to_tokio().await;
385 }
386
387 #[test]
388 fn test_should_convert_to_std_sync() {
389 let temp = NamedTempFile::new().expect("Failed to create temp file");
390
391 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
393
394 let file = SyncRuntime::block_on(File::open(temp.path())).expect("Failed to open file");
395 let _std_file = SyncRuntime::block_on(file.to_tokio());
396 }
397
398 #[tokio::test]
399 async fn test_should_convert_to_tokio_async() {
400 let temp = NamedTempFile::new().expect("Failed to create temp file");
401
402 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
404
405 let file = File::open(temp.path()).await.expect("Failed to open file");
406 let _tokio_file = file.to_tokio().await;
407 }
408
409 #[test]
410 fn test_should_read_sync() {
411 let temp = NamedTempFile::new().expect("Failed to create temp file");
412
413 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
415
416 let mut file = SyncRuntime::block_on(File::open(temp.path())).expect("Failed to open file");
417 let mut buf = vec![0; 11];
418 SyncRuntime::block_on(file.read(&mut buf)).expect("Failed to read file");
419 assert_eq!(buf, b"Hello world");
420 }
421
422 #[tokio::test]
423 async fn test_should_read_async() {
424 let temp = NamedTempFile::new().expect("Failed to create temp file");
425
426 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
428
429 let mut file = File::open(temp.path()).await.expect("Failed to open file");
430 let mut buf = vec![0; 11];
431 file.read(&mut buf).await.expect("Failed to read file");
432 assert_eq!(buf, b"Hello world");
433 }
434
435 #[test]
436 fn test_should_write_sync() {
437 let temp = NamedTempFile::new().expect("Failed to create temp file");
438
439 let mut file =
440 SyncRuntime::block_on(File::create(temp.path())).expect("Failed to open file");
441 SyncRuntime::block_on(file.write(b"Hello world")).expect("Failed to write file");
442 SyncRuntime::block_on(file.flush()).expect("Failed to flush file");
443
444 let buf = std::fs::read(temp.path()).expect("Failed to read file");
445 assert_eq!(buf, b"Hello world");
446 }
447
448 #[tokio::test]
449 async fn test_should_write_async() {
450 let temp = NamedTempFile::new().expect("Failed to create temp file");
451
452 let mut file = File::create(temp.path())
453 .await
454 .expect("Failed to open file");
455 file.write(b"Hello world")
456 .await
457 .expect("Failed to write file");
458 file.flush().await.expect("Failed to flush file");
459
460 let buf = tokio::fs::read(temp.path())
461 .await
462 .expect("Failed to read file");
463 assert_eq!(buf, b"Hello world");
464 }
465
466 #[test]
467 fn test_should_seek_sync() {
468 let temp = NamedTempFile::new().expect("Failed to create temp file");
469
470 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
472
473 let mut file = SyncRuntime::block_on(File::open(temp.path())).expect("Failed to open file");
474 let mut buf = vec![0; 5];
475 SyncRuntime::block_on(file.seek(std::io::SeekFrom::Start(6))).expect("Failed to seek file");
476 SyncRuntime::block_on(file.read(&mut buf)).expect("Failed to read file");
477 assert_eq!(buf, b"world");
478 }
479
480 #[tokio::test]
481 async fn test_should_seek_async() {
482 let temp = NamedTempFile::new().expect("Failed to create temp file");
483
484 std::fs::write(temp.path(), b"Hello world").expect("Failed to write file");
486
487 let mut file = File::open(temp.path()).await.expect("Failed to open file");
488 let mut buf = vec![0; 5];
489 file.seek(std::io::SeekFrom::Start(6))
490 .await
491 .expect("Failed to seek file");
492 file.read(&mut buf).await.expect("Failed to read file");
493 assert_eq!(buf, b"world");
494 }
495}