1use std::collections::HashMap;
4use std::io::IsTerminal;
5use std::process::Stdio;
6use std::sync::Arc;
7
8use crate::{ShellFd, error, sys};
9
10pub trait Stream: std::io::Read + std::io::Write + Send + Sync {
16 fn clone_box(&self) -> Box<dyn Stream>;
18
19 #[cfg(unix)] fn try_clone_to_owned(&self) -> Result<std::os::fd::OwnedFd, error::Error>;
28
29 #[cfg(unix)] fn try_borrow_as_fd(&self) -> Result<std::os::fd::BorrowedFd<'_>, error::Error>;
38}
39
40pub enum OpenFile {
53 Stdin(std::io::Stdin),
55 Stdout(std::io::Stdout),
57 Stderr(std::io::Stderr),
59 File(Arc<std::fs::File>),
61 PipeReader(Arc<std::io::PipeReader>),
63 PipeWriter(Arc<std::io::PipeWriter>),
65 Stream(Box<dyn Stream>),
67}
68
69#[cfg(feature = "serde")]
70impl serde::Serialize for OpenFile {
71 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72 where
73 S: serde::Serializer,
74 {
75 match self {
76 Self::Stdin(_) => serializer.serialize_str("stdin"),
77 Self::Stdout(_) => serializer.serialize_str("stdout"),
78 Self::Stderr(_) => serializer.serialize_str("stderr"),
79 Self::File(_) => serializer.serialize_str("file"),
80 Self::PipeReader(_) => serializer.serialize_str("pipe_reader"),
81 Self::PipeWriter(_) => serializer.serialize_str("pipe_writer"),
82 Self::Stream(_) => serializer.serialize_str("stream"),
83 }
84 }
85}
86
87#[cfg(feature = "serde")]
88impl<'de> serde::Deserialize<'de> for OpenFile {
89 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
90 where
91 D: serde::Deserializer<'de>,
92 {
93 match String::deserialize(deserializer)?.as_str() {
94 "stdin" => return Ok(std::io::stdin().into()),
95 "stdout" => return Ok(std::io::stdout().into()),
96 "stderr" => return Ok(std::io::stderr().into()),
97 "file" => (),
98 "pipe_reader" => (),
99 "pipe_writer" => (),
100 "stream" => (),
101 _ => return Err(serde::de::Error::custom("invalid open file")),
102 }
103
104 null().map_err(serde::de::Error::custom)
107 }
108}
109
110pub fn null() -> Result<OpenFile, error::Error> {
116 let file = sys::fs::open_null_file()?;
117 Ok(file.into())
118}
119
120impl Clone for OpenFile {
121 fn clone(&self) -> Self {
122 match self {
123 Self::Stdin(_) => std::io::stdin().into(),
124 Self::Stdout(_) => std::io::stdout().into(),
125 Self::Stderr(_) => std::io::stderr().into(),
126 Self::File(f) => Self::File(Arc::clone(f)),
129 Self::PipeReader(r) => Self::PipeReader(Arc::clone(r)),
130 Self::PipeWriter(w) => Self::PipeWriter(Arc::clone(w)),
131 Self::Stream(s) => Self::Stream(s.clone_box()),
132 }
133 }
134}
135
136impl std::fmt::Display for OpenFile {
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 match self {
139 Self::Stdin(_) => write!(f, "stdin"),
140 Self::Stdout(_) => write!(f, "stdout"),
141 Self::Stderr(_) => write!(f, "stderr"),
142 Self::File(_) => write!(f, "file"),
143 Self::PipeReader(_) => write!(f, "pipe reader"),
144 Self::PipeWriter(_) => write!(f, "pipe writer"),
145 Self::Stream(_) => write!(f, "stream"),
146 }
147 }
148}
149
150impl OpenFile {
151 #[cfg(unix)] pub(crate) fn try_clone_to_owned(self) -> Result<std::os::fd::OwnedFd, error::Error> {
156 use std::os::fd::AsFd as _;
157
158 match self {
159 Self::Stdin(f) => Ok(f.as_fd().try_clone_to_owned()?),
160 Self::Stdout(f) => Ok(f.as_fd().try_clone_to_owned()?),
161 Self::Stderr(f) => Ok(f.as_fd().try_clone_to_owned()?),
162 Self::File(f) => Ok(f.as_fd().try_clone_to_owned()?),
163 Self::PipeReader(r) => Ok(r.as_fd().try_clone_to_owned()?),
164 Self::PipeWriter(w) => Ok(w.as_fd().try_clone_to_owned()?),
165 Self::Stream(s) => s.try_clone_to_owned(),
166 }
167 }
168
169 #[cfg(unix)] pub fn try_borrow_as_fd(&self) -> Result<std::os::fd::BorrowedFd<'_>, error::Error> {
177 use std::os::fd::AsFd as _;
178
179 match self {
180 Self::Stdin(f) => Ok(f.as_fd()),
181 Self::Stdout(f) => Ok(f.as_fd()),
182 Self::Stderr(f) => Ok(f.as_fd()),
183 Self::File(f) => Ok(f.as_fd()),
184 Self::PipeReader(r) => Ok(r.as_fd()),
185 Self::PipeWriter(w) => Ok(w.as_fd()),
186 Self::Stream(s) => s.try_borrow_as_fd(),
187 }
188 }
189
190 pub(crate) fn is_dir(&self) -> bool {
191 match self {
192 Self::Stdin(_) | Self::Stdout(_) | Self::Stderr(_) => false,
193 Self::File(file) => file.metadata().is_ok_and(|m| m.is_dir()),
194 Self::PipeReader(_) | Self::PipeWriter(_) | Self::Stream(_) => false,
195 }
196 }
197
198 pub fn is_terminal(&self) -> bool {
200 match self {
201 Self::Stdin(f) => f.is_terminal(),
202 Self::Stdout(f) => f.is_terminal(),
203 Self::Stderr(f) => f.is_terminal(),
204 Self::File(f) => f.is_terminal(),
205 Self::PipeReader(_) | Self::PipeWriter(_) | Self::Stream(_) => false,
206 }
207 }
208}
209
210impl From<std::io::Stdin> for OpenFile {
211 fn from(stdin: std::io::Stdin) -> Self {
213 Self::Stdin(stdin)
214 }
215}
216
217impl From<std::io::Stdout> for OpenFile {
218 fn from(stdout: std::io::Stdout) -> Self {
220 Self::Stdout(stdout)
221 }
222}
223
224impl From<std::io::Stderr> for OpenFile {
225 fn from(stderr: std::io::Stderr) -> Self {
227 Self::Stderr(stderr)
228 }
229}
230
231impl From<std::fs::File> for OpenFile {
232 fn from(file: std::fs::File) -> Self {
233 Self::File(Arc::new(file))
234 }
235}
236
237impl From<std::io::PipeReader> for OpenFile {
238 fn from(reader: std::io::PipeReader) -> Self {
239 Self::PipeReader(Arc::new(reader))
240 }
241}
242
243impl From<std::io::PipeWriter> for OpenFile {
244 fn from(writer: std::io::PipeWriter) -> Self {
245 Self::PipeWriter(Arc::new(writer))
246 }
247}
248
249impl TryFrom<OpenFile> for Stdio {
250 type Error = error::Error;
251
252 fn try_from(open_file: OpenFile) -> Result<Self, Self::Error> {
253 match open_file {
260 OpenFile::Stdin(_) | OpenFile::Stdout(_) | OpenFile::Stderr(_) => Ok(Self::inherit()),
261 OpenFile::File(f) => Ok(f.try_clone()?.into()),
262 OpenFile::PipeReader(r) => Ok(r.try_clone()?.into()),
263 OpenFile::PipeWriter(w) => Ok(w.try_clone()?.into()),
264 OpenFile::Stream(_) => Ok(Self::null()),
266 }
267 }
268}
269
270impl std::io::Read for OpenFile {
271 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
272 match self {
273 Self::Stdin(f) => f.read(buf),
274 Self::Stdout(_) => Err(std::io::Error::other(
275 error::ErrorKind::OpenFileNotReadable("stdout"),
276 )),
277 Self::Stderr(_) => Err(std::io::Error::other(
278 error::ErrorKind::OpenFileNotReadable("stderr"),
279 )),
280 Self::File(f) => f.as_ref().read(buf),
283 Self::PipeReader(reader) => reader.as_ref().read(buf),
284 Self::PipeWriter(_) => Err(std::io::Error::other(
285 error::ErrorKind::OpenFileNotReadable("pipe writer"),
286 )),
287 Self::Stream(s) => s.read(buf),
288 }
289 }
290}
291
292impl std::io::Write for OpenFile {
293 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
294 match self {
295 Self::Stdin(_) => Err(std::io::Error::other(
296 error::ErrorKind::OpenFileNotWritable("stdin"),
297 )),
298 Self::Stdout(f) => f.write(buf),
299 Self::Stderr(f) => f.write(buf),
300 Self::File(f) => f.as_ref().write(buf),
303 Self::PipeReader(_) => Err(std::io::Error::other(
304 error::ErrorKind::OpenFileNotWritable("pipe reader"),
305 )),
306 Self::PipeWriter(writer) => writer.as_ref().write(buf),
307 Self::Stream(s) => s.write(buf),
308 }
309 }
310
311 fn flush(&mut self) -> std::io::Result<()> {
312 match self {
313 Self::Stdin(_) => Ok(()),
314 Self::Stdout(f) => f.flush(),
315 Self::Stderr(f) => f.flush(),
316 Self::File(f) => f.as_ref().flush(),
317 Self::PipeReader(_) => Ok(()),
318 Self::PipeWriter(writer) => writer.as_ref().flush(),
319 Self::Stream(s) => s.flush(),
320 }
321 }
322}
323
324pub enum OpenFileEntry<'a> {
326 Open(&'a OpenFile),
328 NotPresent,
331 NotSpecified,
334}
335
336#[derive(Clone, Default)]
338#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
339pub struct OpenFiles {
340 files: HashMap<ShellFd, Option<OpenFile>>,
342}
343
344impl OpenFiles {
345 pub const STDIN_FD: ShellFd = 0;
347 pub const STDOUT_FD: ShellFd = 1;
349 pub const STDERR_FD: ShellFd = 2;
351
352 const FIRST_NON_STDIO_FD: ShellFd = 3;
354 const MAX_FD: ShellFd = 1024;
356
357 pub(crate) fn new() -> Self {
360 Self {
361 files: HashMap::from([
362 (Self::STDIN_FD, Some(std::io::stdin().into())),
363 (Self::STDOUT_FD, Some(std::io::stdout().into())),
364 (Self::STDERR_FD, Some(std::io::stderr().into())),
365 ]),
366 }
367 }
368
369 pub fn update_from(&mut self, files: impl Iterator<Item = (ShellFd, OpenFile)>) {
378 for (fd, file) in files {
379 let _ = self.files.insert(fd, Some(file));
380 }
381 }
382
383 pub fn try_stdin(&self) -> Option<&OpenFile> {
385 self.files.get(&Self::STDIN_FD).and_then(|f| f.as_ref())
386 }
387
388 pub fn try_stdout(&self) -> Option<&OpenFile> {
390 self.files.get(&Self::STDOUT_FD).and_then(|f| f.as_ref())
391 }
392
393 pub fn try_stderr(&self) -> Option<&OpenFile> {
395 self.files.get(&Self::STDERR_FD).and_then(|f| f.as_ref())
396 }
397
398 pub fn remove_fd(&mut self, fd: ShellFd) -> Option<OpenFile> {
406 self.files.insert(fd, None).and_then(|f| f)
407 }
408
409 pub fn try_fd(&self, fd: ShellFd) -> Option<&OpenFile> {
416 self.files.get(&fd).and_then(|f| f.as_ref())
417 }
418
419 pub fn fd_entry(&self, fd: ShellFd) -> OpenFileEntry<'_> {
427 self.files
428 .get(&fd)
429 .map_or(OpenFileEntry::NotSpecified, |opt_file| match opt_file {
430 Some(f) => OpenFileEntry::Open(f),
431 None => OpenFileEntry::NotPresent,
432 })
433 }
434
435 pub fn contains_fd(&self, fd: ShellFd) -> bool {
437 self.files.contains_key(&fd)
438 }
439
440 pub fn set_fd(&mut self, fd: ShellFd, file: OpenFile) -> Option<OpenFile> {
449 self.files.insert(fd, Some(file)).and_then(|f| f)
450 }
451
452 pub fn iter_fds(&self) -> impl Iterator<Item = (ShellFd, &OpenFile)> {
454 self.files
455 .iter()
456 .filter_map(|(fd, file)| file.as_ref().map(|f| (*fd, f)))
457 }
458
459 pub fn add(&mut self, file: OpenFile) -> Result<ShellFd, error::Error> {
470 let mut fd = Self::FIRST_NON_STDIO_FD;
472 while self.files.contains_key(&fd) {
473 if fd >= Self::MAX_FD {
474 return Err(error::ErrorKind::TooManyOpenFiles.into());
475 }
476
477 fd += 1;
478 }
479
480 self.files.insert(fd, Some(file));
481 Ok(fd)
482 }
483}
484
485impl<I> From<I> for OpenFiles
486where
487 I: Iterator<Item = (ShellFd, OpenFile)>,
488{
489 fn from(iter: I) -> Self {
490 let files = iter.map(|(fd, file)| (fd, Some(file))).collect();
491 Self { files }
492 }
493}