deno_core/io/
resource_handle.rs

1// Copyright 2018-2025 the Deno authors. MIT license.
2
3use std::io::IsTerminal;
4
5/// Represents an underlying handle for a platform. On unix, everything is an `fd`. On Windows, everything
6/// is a Windows handle except for sockets (which are `SOCKET`s).
7#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
8#[allow(unused)]
9pub enum ResourceHandle {
10  /// A file handle/descriptor.
11  Fd(ResourceHandleFd),
12  /// A socket handle/file descriptor.
13  Socket(ResourceHandleSocket),
14}
15
16#[cfg(unix)]
17pub type ResourceHandleFd = std::os::fd::RawFd;
18#[cfg(unix)]
19pub type ResourceHandleSocket = std::os::fd::RawFd;
20#[cfg(windows)]
21pub type ResourceHandleFd = std::os::windows::io::RawHandle;
22#[cfg(windows)]
23pub type ResourceHandleSocket = std::os::windows::io::RawSocket;
24
25impl ResourceHandle {
26  /// Converts a file-like thing to a [`ResourceHandle`].
27  #[cfg(windows)]
28  pub fn from_fd_like(io: &impl std::os::windows::io::AsRawHandle) -> Self {
29    Self::Fd(io.as_raw_handle())
30  }
31
32  /// Converts a file-like thing to a [`ResourceHandle`].
33  #[cfg(unix)]
34  pub fn from_fd_like(io: &impl std::os::unix::io::AsRawFd) -> Self {
35    Self::Fd(io.as_raw_fd())
36  }
37
38  /// Converts a socket-like thing to a [`ResourceHandle`].
39  #[cfg(windows)]
40  pub fn from_socket_like(io: &impl std::os::windows::io::AsRawSocket) -> Self {
41    Self::Socket(io.as_raw_socket())
42  }
43
44  /// Converts a socket-like thing to a [`ResourceHandle`].
45  #[cfg(unix)]
46  pub fn from_socket_like(io: &impl std::os::unix::io::AsRawFd) -> Self {
47    Self::Socket(io.as_raw_fd())
48  }
49
50  /// Runs a basic validity check on the handle, but cannot fully determine if the handle is valid for use.
51  pub fn is_valid(&self) -> bool {
52    #[cfg(windows)]
53    {
54      match self {
55        // NULL or INVALID_HANDLE_VALUE
56        Self::Fd(handle) => {
57          !handle.is_null()
58            && *handle != -1_isize as std::os::windows::io::RawHandle
59        }
60        // INVALID_SOCKET
61        Self::Socket(socket) => {
62          *socket != -1_i64 as std::os::windows::io::RawSocket
63        }
64      }
65    }
66    #[cfg(unix)]
67    {
68      match self {
69        Self::Fd(fd) => *fd >= 0,
70        Self::Socket(fd) => *fd >= 0,
71      }
72    }
73  }
74
75  /// Returns this as a file-descriptor-like handle.
76  pub fn as_fd_like(&self) -> Option<ResourceHandleFd> {
77    match self {
78      Self::Fd(fd) => Some(*fd),
79      _ => None,
80    }
81  }
82
83  /// Returns this as a socket-like handle.
84  pub fn as_socket_like(&self) -> Option<ResourceHandleSocket> {
85    match self {
86      Self::Socket(socket) => Some(*socket),
87      _ => None,
88    }
89  }
90
91  /// Determines if this handle is a terminal. Analagous to [`std::io::IsTerminal`].
92  pub fn is_terminal(&self) -> bool {
93    match self {
94      Self::Fd(fd) if self.is_valid() => {
95        #[cfg(windows)]
96        {
97          // SAFETY: The resource remains open for the for the duration of borrow_raw
98          unsafe {
99            std::os::windows::io::BorrowedHandle::borrow_raw(*fd).is_terminal()
100          }
101        }
102        #[cfg(unix)]
103        {
104          // SAFETY: The resource remains open for the for the duration of borrow_raw
105          unsafe { std::os::fd::BorrowedFd::borrow_raw(*fd).is_terminal() }
106        }
107      }
108      _ => false,
109    }
110  }
111}