char_device/
char_device.rs1use 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#[derive(Debug)]
28#[repr(transparent)]
29pub struct CharDevice(std::fs::File);
30
31impl CharDevice {
32 #[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 #[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 #[inline]
79 pub unsafe fn new_unchecked<Filelike: IntoFilelike>(filelike: Filelike) -> Self {
80 Self(File::from_into_filelike(filelike))
81 }
82
83 #[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 #[inline]
101 pub fn try_clone(&self) -> io::Result<Self> {
102 self.0.try_clone().map(Self)
103 }
104
105 #[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 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}