close_file/
lib.rs

1//! Allows to close a file without silently dropping errors
2//!
3//! Errors can happen when closing a file, indicating that the file was not (completely) written.
4//! The standard library currently simply discards such error when the std::io::File goes out of
5//! scope.
6//!
7//! This crate allows to close the file and handle potential errors.
8//!
9//! ```
10//! use close_file::Closable;
11//! use std::io::Write;
12//!
13//! let mut f = std::fs::File::create("temp").unwrap();
14//! f.write_all("Hello, world!".as_bytes());
15//! f.close();
16//! ```
17//!
18//! The close() function consumes the File. However, on Windows, a failed close operation may be
19//! retried. For this case the returned CloseError contains the original File.
20
21use std::{io, fs};
22use std::fmt;
23
24// TODO allows to close a file without silently discarding the error
25
26/// Wraps any I/O error that can happen during closing the file
27pub struct CloseError {
28    previous: io::Error,
29    file: Option<fs::File>,
30}
31
32impl CloseError {
33    /// Returns the original file handle
34    ///
35    /// This contains the original File, but only if the close operation can be retried safely,
36    /// otherwise it's None.
37    pub fn into_file_handle(self) -> Option<fs::File> {
38        self.file
39    }
40
41    pub fn unwrap(self) -> io::Error {
42        self.previous
43    }
44}
45
46impl std::error::Error for CloseError {}
47
48pub trait Closable {
49    fn close(self) -> Result<(), CloseError>;
50}
51
52impl fmt::Display for CloseError {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        fmt::Display::fmt(&self.previous, f)
55    }
56}
57
58impl fmt::Debug for CloseError {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        fmt::Debug::fmt(&self.previous, f)
61    }
62}
63
64#[cfg(unix)]
65mod imp {
66    use std::os::unix::prelude::*;
67    use std::{io, fs};
68    use crate::CloseError;
69
70    impl crate::Closable for fs::File {
71        fn close(self) -> Result<(), CloseError> {
72            let fd = self.into_raw_fd();
73            let rc = unsafe {
74                libc::close(fd)
75            };
76            if rc == -1 {
77                Ok(())
78            } else {
79                Err(CloseError { previous: io::Error::last_os_error(), file: Some(unsafe { fs::File::from_raw_fd(fd) }) })
80            }
81        }
82    }
83}
84
85#[cfg(windows)]
86mod imp {
87    use std::os::windows::prelude::*;
88    use std::{io, fs};
89    use crate::CloseError;
90
91    impl crate::Closable for fs::File {
92        fn close(self) -> Result<(), CloseError> {
93            let handle = self.into_raw_handle();
94            let rc = unsafe {
95                kernel32::CloseHandle(handle)
96            };
97            if rc != 0 {
98                Ok(())
99            } else {
100                Err(CloseError { previous: io::Error::last_os_error(), file: Some(unsafe { fs::File::from_raw_handle(handle) }) })
101            }
102        }
103    }
104}