[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
//! Provides an extension trait ([`Close`]) for closing I/O related
//! types containing a resource which may return an error when closed.
//!
//! 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).
//!
//! such as [`File`](std::fs::File) and
//! [`BufWriter`](std::io::BufWriter).
//!
//! Using [`Close`] enables the handling 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 = "hello world".as_bytes();
//!     let mut buffer = BufWriter::new(tempfile::tempfile()?);
//!     buffer.write_all(data)?;
//!     buffer.close()?;
//!     Ok(())
//! }
//! ```

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

/// An extension trait for closing I/O related types containing a
/// resource which may return an error when closed.
pub trait Close {
    /// Consumes and closes an I/O related type and its containing
    /// resource (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)]
        /// Consumes and closes a type containing a raw file descriptor.
        impl<$($ty_param),*> Close for $ty {
            fn close(self) -> Result<()> {
                let fd = self.into_raw_fd();
                let ret = unsafe { libc::close(fd) };
                if ret == 0 { Ok(()) } else { Err(Error::last_os_error()) }
            }
        }
    };
}

macro_rules! windows_impl_close_raw_handle {
    ($ty:ty $(,$ty_param:ident)*) => {
        #[cfg(windows)]
        /// Consumes and closes a type containing a raw handle.
        impl<$($ty_param),*> Close for $ty {
            fn close(self) -> Result<()> {
                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<()> {
                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<R: Read + Close> Close for BufReader<R> {
    /// Consumes and closes a [`BufReader`](std::io::BufReader), and its
    /// contained [`Read`](std::io::Read) instance.
    fn close(self) -> Result<()> {
        self.into_inner().close()
    }
}

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()
    }
}