char_device/
char_device.rs

1use io_lifetimes::{FromFilelike, IntoFilelike};
2use std::fmt::Arguments;
3use std::fs::{File, OpenOptions};
4use std::io::{self, IoSlice, IoSliceMut, Read, Write};
5use std::path::Path;
6#[cfg(not(windows))]
7use {
8    io_extras::os::rustix::{AsRawFd, AsRawReadWriteFd, AsReadWriteFd, IntoRawFd, RawFd},
9    io_lifetimes::{AsFd, BorrowedFd, OwnedFd},
10    rustix::fs::FileTypeExt,
11};
12#[cfg(windows)]
13use {
14    io_extras::os::windows::{
15        AsHandleOrSocket, AsRawHandleOrSocket, AsRawReadWriteHandleOrSocket,
16        AsReadWriteHandleOrSocket, BorrowedHandleOrSocket, IntoRawHandleOrSocket,
17        OwnedHandleOrSocket, RawHandleOrSocket,
18    },
19    io_lifetimes::{AsHandle, BorrowedHandle, OwnedHandle},
20    std::os::windows::io::{AsRawHandle, IntoRawHandle, RawHandle},
21};
22
23/// An unbuffered character device.
24///
25/// This is a wrapper around [`std::fs::File`] which is intended for use with
26/// character device "files" such as "/dev/tty".
27#[derive(Debug)]
28#[repr(transparent)]
29pub struct CharDevice(std::fs::File);
30
31impl CharDevice {
32    /// Construct a new `CharDevice`. Fail if the given handle isn't a valid
33    /// handle for a character device, or it can't be determined.
34    #[inline]
35    pub fn new<Filelike: IntoFilelike + Read + Write>(filelike: Filelike) -> io::Result<Self> {
36        Self::_new(File::from_into_filelike(filelike))
37    }
38
39    fn _new(file: File) -> io::Result<Self> {
40        #[cfg(not(windows))]
41        {
42            let file_type = file.metadata()?.file_type();
43            if !file_type.is_char_device() {
44                return Err(io::Error::new(
45                    io::ErrorKind::Other,
46                    "raw fd is not a char device",
47                ));
48            }
49        }
50
51        #[cfg(windows)]
52        {
53            let file_type = winx::winapi_util::file::typ(&file)?;
54            if !file_type.is_char() {
55                return Err(io::Error::new(
56                    io::ErrorKind::Other,
57                    "raw handle is not a char device",
58                ));
59            }
60        }
61
62        Ok(Self(file))
63    }
64
65    /// Construct a new `CharDevice` from the given filename. Fail if the given
66    /// handle isn't a valid handle for a character device, or it can't be
67    /// determined.
68    #[inline]
69    pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
70        Self::new(OpenOptions::new().read(true).write(true).open(path)?)
71    }
72
73    /// Construct a new `CharDevice`.
74    ///
75    /// # Safety
76    ///
77    /// Doesn't check that the handle is valid or a character device.
78    #[inline]
79    pub unsafe fn new_unchecked<Filelike: IntoFilelike>(filelike: Filelike) -> Self {
80        Self(File::from_into_filelike(filelike))
81    }
82
83    /// Construct a new `CharDevice` which discards writes and reads nothing.
84    ///
85    /// This is "/dev/null" on Posix-ish platforms and "nul" on Windows.
86    #[inline]
87    pub fn null() -> io::Result<Self> {
88        #[cfg(unix)]
89        {
90            Self::open("/dev/null")
91        }
92
93        #[cfg(windows)]
94        {
95            Self::open("nul")
96        }
97    }
98
99    /// Creates a new independently owned handle to the underlying device.
100    #[inline]
101    pub fn try_clone(&self) -> io::Result<Self> {
102        self.0.try_clone().map(Self)
103    }
104
105    /// Return the number of bytes which are ready to be read immediately.
106    #[inline]
107    pub fn num_ready_bytes(&self) -> io::Result<u64> {
108        #[cfg(not(windows))]
109        {
110            Ok(rustix::io::ioctl_fionread(self)?)
111        }
112
113        #[cfg(windows)]
114        {
115            // Return the conservatively correct result.
116            Ok(0)
117        }
118    }
119}
120
121impl Read for CharDevice {
122    #[inline]
123    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
124        self.0.read(buf)
125    }
126
127    #[inline]
128    fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
129        self.0.read_vectored(bufs)
130    }
131
132    #[cfg(can_vector)]
133    #[inline]
134    fn is_read_vectored(&self) -> bool {
135        self.0.is_read_vectored()
136    }
137
138    #[inline]
139    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
140        self.0.read_to_end(buf)
141    }
142
143    #[inline]
144    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
145        self.0.read_to_string(buf)
146    }
147
148    #[inline]
149    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
150        self.0.read_exact(buf)
151    }
152}
153
154impl Write for CharDevice {
155    #[inline]
156    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
157        self.0.write(buf)
158    }
159
160    #[inline]
161    fn flush(&mut self) -> io::Result<()> {
162        self.0.flush()
163    }
164
165    #[inline]
166    fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
167        self.0.write_vectored(bufs)
168    }
169
170    #[cfg(can_vector)]
171    #[inline]
172    fn is_write_vectored(&self) -> bool {
173        self.0.is_write_vectored()
174    }
175
176    #[inline]
177    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
178        self.0.write_all(buf)
179    }
180
181    #[cfg(write_all_vectored)]
182    #[inline]
183    fn write_all_vectored(&mut self, bufs: &mut [IoSlice]) -> io::Result<()> {
184        self.0.write_all_vectored(bufs)
185    }
186
187    #[inline]
188    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
189        self.0.write_fmt(fmt)
190    }
191}
192
193#[cfg(not(windows))]
194impl AsRawFd for CharDevice {
195    #[inline]
196    fn as_raw_fd(&self) -> RawFd {
197        self.0.as_raw_fd()
198    }
199}
200
201#[cfg(not(windows))]
202impl AsFd for CharDevice {
203    #[inline]
204    fn as_fd(&self) -> BorrowedFd<'_> {
205        self.0.as_fd()
206    }
207}
208
209#[cfg(windows)]
210impl AsRawHandle for CharDevice {
211    #[inline]
212    fn as_raw_handle(&self) -> RawHandle {
213        self.0.as_raw_handle()
214    }
215}
216
217#[cfg(windows)]
218impl AsHandle for CharDevice {
219    #[inline]
220    fn as_handle(&self) -> BorrowedHandle<'_> {
221        self.0.as_handle()
222    }
223}
224
225#[cfg(windows)]
226impl AsHandleOrSocket for CharDevice {
227    #[inline]
228    fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
229        BorrowedHandleOrSocket::from_handle(self.0.as_handle())
230    }
231}
232
233#[cfg(windows)]
234impl AsRawHandleOrSocket for CharDevice {
235    #[inline]
236    fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
237        self.0.as_raw_handle_or_socket()
238    }
239}
240
241#[cfg(not(windows))]
242impl IntoRawFd for CharDevice {
243    #[inline]
244    fn into_raw_fd(self) -> RawFd {
245        self.0.into_raw_fd()
246    }
247}
248
249#[cfg(not(windows))]
250impl From<CharDevice> for OwnedFd {
251    #[inline]
252    fn from(char_device: CharDevice) -> OwnedFd {
253        char_device.0.into()
254    }
255}
256
257#[cfg(windows)]
258impl IntoRawHandle for CharDevice {
259    #[inline]
260    fn into_raw_handle(self) -> RawHandle {
261        self.0.into_raw_handle()
262    }
263}
264
265#[cfg(windows)]
266impl From<CharDevice> for OwnedHandle {
267    #[inline]
268    fn from(char_device: CharDevice) -> OwnedHandle {
269        char_device.0.into()
270    }
271}
272
273#[cfg(windows)]
274impl IntoRawHandleOrSocket for CharDevice {
275    #[inline]
276    fn into_raw_handle_or_socket(self) -> RawHandleOrSocket {
277        self.0.into_raw_handle_or_socket()
278    }
279}
280
281#[cfg(windows)]
282impl From<CharDevice> for OwnedHandleOrSocket {
283    #[inline]
284    fn from(char_device: CharDevice) -> Self {
285        char_device.0.into()
286    }
287}
288
289#[cfg(not(windows))]
290impl AsRawReadWriteFd for CharDevice {
291    #[inline]
292    fn as_raw_read_fd(&self) -> RawFd {
293        self.as_raw_fd()
294    }
295
296    #[inline]
297    fn as_raw_write_fd(&self) -> RawFd {
298        self.as_raw_fd()
299    }
300}
301
302#[cfg(not(windows))]
303impl AsReadWriteFd for CharDevice {
304    #[inline]
305    fn as_read_fd(&self) -> BorrowedFd<'_> {
306        self.as_fd()
307    }
308
309    #[inline]
310    fn as_write_fd(&self) -> BorrowedFd<'_> {
311        self.as_fd()
312    }
313}
314
315#[cfg(windows)]
316impl AsRawReadWriteHandleOrSocket for CharDevice {
317    #[inline]
318    fn as_raw_read_handle_or_socket(&self) -> RawHandleOrSocket {
319        self.as_raw_handle_or_socket()
320    }
321
322    #[inline]
323    fn as_raw_write_handle_or_socket(&self) -> RawHandleOrSocket {
324        self.as_raw_handle_or_socket()
325    }
326}
327
328#[cfg(windows)]
329impl AsReadWriteHandleOrSocket for CharDevice {
330    #[inline]
331    fn as_read_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
332        self.as_handle_or_socket()
333    }
334
335    #[inline]
336    fn as_write_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
337        self.as_handle_or_socket()
338    }
339}