[go: up one dir, main page]

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
//! An extension trait ([`Close`]) for safely closing I/O related types
//! such as [`File`](std::fs::File) and
//! [`BufWriter`](std::io::BufWriter).  Specifically, it is for types
//! containing a resource handle (such as a file descriptor) which, when
//! closed, may return an I/O error.
//!
//! Provided implementations are for standard library types which
//! implement (or contain types which implement):
//!
//! - [`IntoRawFd`](https://doc.rust-lang.org/nightly/std/os/unix/io/trait.IntoRawFd.html) (Unix)
//!
//! - [`IntoRawHandle`](https://doc.rust-lang.org/nightly/std/os/windows/io/trait.IntoRawHandle.html) or
//!   [`IntoRawSocket`](https://doc.rust-lang.org/nightly/std/os/windows/io/trait.IntoRawSocket.html) (Windows).
//!
//! Using [`Close`] enables the handling or reporting of I/O errors
//! which might otherwise be ignored during drop.
//!
//! # BufWriter example
//!
//! ```
//! use std::io::{BufWriter, Result, Write};
//! use io_close::Close;
//!
//! fn main() -> Result<()> {
//!     let data = b"hello world";
//!     let mut buffer = BufWriter::new(tempfile::tempfile()?);
//!     buffer.write_all(data)?;
//!     buffer.close()?; // consume and close buffer and its contained File
//!     Ok(())
//! }
//! ```

use std::io::{BufWriter, Error, LineWriter, Result, Write};
#[cfg(windows)]
use std::os::{raw::c_int, windows::raw::SOCKET};
#[cfg(windows)]
extern "system" {
    fn closesocket(socket: SOCKET) -> c_int;
    fn WSAGetLastError() -> c_int;
}

pub mod fs;

/// An extension trait for safely closing I/O related types containing a
/// resource handle which, when closed, may return an I/O error.
pub trait Close {
    /// Consumes and closes an I/O related type and its contained
    /// resource handle (such as a file descriptor). If any I/O errors
    /// occur, the first such error is returned.
    fn close(self) -> Result<()>;
}

macro_rules! unix_impl_close_raw_fd {
    ($ty:ty $(,$ty_param:ident)*) => {
        #[cfg(unix)]
        impl<$($ty_param),*> Close for $ty {
            /// Consumes and closes a type containing a raw file descriptor.
            fn close(self) -> Result<()> {
                use std::{io::ErrorKind, os::unix::io::IntoRawFd};

                let fd = self.into_raw_fd();
                let ret = unsafe { libc::close(fd) };
                if ret == 0 {
                    Ok(())
                } else {
                    match Error::last_os_error() {
                        e if e.kind() == ErrorKind::Interrupted => Ok(()),
                        e => Err(e),
                    }
                }
            }
        }
    };
}

macro_rules! windows_impl_close_raw_handle {
    ($ty:ty $(,$ty_param:ident)*) => {
        #[cfg(windows)]
        impl<$($ty_param),*> Close for $ty {
            /// Consumes and closes a type containing a raw handle.
            fn close(self) -> Result<()> {
                use std::os::windows::io::IntoRawHandle;

                let handle = self.into_raw_handle();
                let ret = unsafe { kernel32::CloseHandle(handle) };
                if ret != 0 { Ok(()) } else { Err(Error::last_os_error()) }
            }
        }
    };
}

macro_rules! windows_impl_close_raw_socket {
    ($ty:ty $(,$ty_param:ident)*) => {
        #[cfg(windows)]
        impl<$($ty_param),*> Close for $ty {
            /// Consumes and closes a type containing a raw socket.
            fn close(self) -> Result<()> {
                use std::os::windows::io::IntoRawSocket;

                let socket = self.into_raw_socket();
                let ret = unsafe { closesocket(socket) };
                if ret == 0 {
                    Ok(())
                } else {
                    Err(Error::from_raw_os_error(unsafe { WSAGetLastError() }))
                }
            }
        }
    };
}

unix_impl_close_raw_fd!(std::fs::File);
unix_impl_close_raw_fd!(std::net::TcpListener);
unix_impl_close_raw_fd!(std::net::TcpStream);
unix_impl_close_raw_fd!(std::net::UdpSocket);
unix_impl_close_raw_fd!(std::os::unix::io::RawFd);
unix_impl_close_raw_fd!(std::os::unix::net::UnixDatagram);
unix_impl_close_raw_fd!(std::os::unix::net::UnixListener);
unix_impl_close_raw_fd!(std::os::unix::net::UnixStream);
unix_impl_close_raw_fd!(std::process::ChildStderr);
unix_impl_close_raw_fd!(std::process::ChildStdin);
unix_impl_close_raw_fd!(std::process::ChildStdout);

windows_impl_close_raw_handle!(std::fs::File);
windows_impl_close_raw_handle!(std::process::Child);
windows_impl_close_raw_handle!(std::process::ChildStderr);
windows_impl_close_raw_handle!(std::process::ChildStdin);
windows_impl_close_raw_handle!(std::process::ChildStdout);
windows_impl_close_raw_handle!(std::thread::JoinHandle<T>, T);

windows_impl_close_raw_socket!(std::net::TcpListener);
windows_impl_close_raw_socket!(std::net::TcpStream);
windows_impl_close_raw_socket!(std::net::UdpSocket);

impl<W: Write + Close> Close for BufWriter<W> {
    /// Consumes and closes a [`BufWriter`](std::io::BufWriter) and its
    /// contained [`Write`](std::io::Write) instance. The
    /// [`BufWriter`](std::io::BufWriter) is flushed before closing. If
    /// any I/O errors occur, the first such error is returned.
    fn close(self) -> Result<()> {
        self.into_inner()?.close()
    }
}

impl<W: Write + Close> Close for LineWriter<W> {
    /// Consumes and closes a [`LineWriter`](std::io::LineWriter) and
    /// its contained [`Write`](std::io::Write) instance. The
    /// [`LineWriter`](std::io::LineWriter) is flushed before
    /// closing. If any I/O errors occur, the first such error is
    /// returned.
    fn close(self) -> Result<()> {
        self.into_inner()?.close()
    }
}