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
use std::any::Any;
use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
use filedescriptor::{AsRawFileDescriptor, FileDescriptor, StdioDescriptor};
static REDIRECT_FLAGS: [AtomicBool; 3] = [
AtomicBool::new(false),
AtomicBool::new(false),
AtomicBool::new(false),
];
pub struct RedirectError<F> {
pub error: io::Error,
pub file: F,
}
impl<F> From<RedirectError<F>> for io::Error {
fn from(err: RedirectError<F>) -> io::Error {
err.error
}
}
impl<F: Any> ::std::error::Error for RedirectError<F> {
fn description(&self) -> &str {
self.error.description()
}
fn cause(&self) -> Option<&::std::error::Error> {
Some(&self.error)
}
}
impl<F> ::std::fmt::Display for RedirectError<F> {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
self.error.fmt(fmt)
}
}
impl<F> ::std::fmt::Debug for RedirectError<F> {
fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> {
self.error.fmt(fmt)
}
}
pub struct Redirect<F> {
#[allow(dead_code)]
fds: RedirectFds,
file: F,
}
struct RedirectFds {
std_fd: FileDescriptor,
stdio: StdioDescriptor,
}
impl RedirectFds {
fn make<F: AsRawFileDescriptor>(file: &F, stdio: StdioDescriptor) -> io::Result<RedirectFds> {
if REDIRECT_FLAGS[stdio as usize].fetch_or(true, Ordering::Relaxed) {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"Redirect already exists.",
));
}
let std_fd = FileDescriptor::redirect_stdio(file, stdio)
.map_err(|error| io::Error::new(io::ErrorKind::Other, error.to_string()))?;
Ok(RedirectFds { std_fd, stdio })
}
}
impl Drop for RedirectFds {
fn drop(&mut self) {
let _ = FileDescriptor::redirect_stdio(&self.std_fd, self.stdio);
REDIRECT_FLAGS[self.stdio as usize].store(false, Ordering::Relaxed);
}
}
impl<F> Redirect<F>
where
F: AsRawFileDescriptor,
{
fn make(file: F, stdio: StdioDescriptor) -> Result<Self, RedirectError<F>> {
let fds = match RedirectFds::make(&file, stdio) {
Ok(fds) => fds,
Err(error) => return Err(RedirectError { error, file }),
};
Ok(Redirect { fds, file })
}
pub fn stdout(file: F) -> Result<Self, RedirectError<F>> {
Redirect::make(file, StdioDescriptor::Stdout)
}
pub fn stderr(file: F) -> Result<Self, RedirectError<F>> {
Redirect::make(file, StdioDescriptor::Stderr)
}
pub fn into_inner(self) -> F {
self.file
}
}