terminal_io/
terminal_writer.rs1use crate::config::{detect_write_config, WriteConfig};
4use crate::{Terminal, TerminalColorSupport, WriteTerminal};
5use io_extras::grip::AsGrip;
6#[cfg(windows)]
7use io_extras::os::windows::{
8 AsHandleOrSocket, AsRawHandleOrSocket, BorrowedHandleOrSocket, RawHandleOrSocket,
9};
10use std::fmt;
11use std::io::{self, IoSlice, Write};
12#[cfg(not(windows))]
13use {
14 io_extras::os::rustix::{AsRawFd, RawFd},
15 std::os::fd::{AsFd, BorrowedFd},
16};
17
18#[derive(Debug)]
20pub struct TerminalWriter<Inner: Write> {
21 inner: Inner,
22 write_config: Option<WriteConfig>,
23}
24
25impl<Inner: Write + AsGrip> TerminalWriter<Inner> {
26 pub fn with_handle(inner: Inner) -> Self {
29 let write_config = detect_write_config(&inner);
30 Self {
31 inner,
32 write_config,
33 }
34 }
35
36 pub fn from(
39 inner: Inner,
40 is_terminal: bool,
41 color_support: TerminalColorSupport,
42 color_preference: bool,
43 ) -> Self {
44 Self {
45 inner,
46 write_config: if is_terminal {
47 Some(WriteConfig {
48 color_support,
49 color_preference,
50 })
51 } else {
52 None
53 },
54 }
55 }
56}
57
58impl<Inner: Write> TerminalWriter<Inner> {
59 pub fn generic(inner: Inner) -> Self {
62 Self {
63 inner,
64 write_config: None,
65 }
66 }
67
68 #[inline]
70 pub fn into_inner(self) -> Inner {
71 self.inner
72 }
73}
74
75#[cfg(not(windows))]
76impl<Inner: Write + AsRawFd> AsRawFd for TerminalWriter<Inner> {
77 #[inline]
78 fn as_raw_fd(&self) -> RawFd {
79 self.inner.as_raw_fd()
80 }
81}
82
83#[cfg(not(windows))]
84impl<Inner: Write + AsFd> AsFd for TerminalWriter<Inner> {
85 #[inline]
86 fn as_fd(&self) -> BorrowedFd {
87 self.inner.as_fd()
88 }
89}
90
91#[cfg(windows)]
92impl<Inner: Write + AsRawHandleOrSocket> AsRawHandleOrSocket for TerminalWriter<Inner> {
93 #[inline]
94 fn as_raw_handle_or_socket(&self) -> RawHandleOrSocket {
95 self.inner.as_raw_handle_or_socket()
96 }
97}
98
99#[cfg(windows)]
100impl<Inner: Write + AsHandleOrSocket> AsHandleOrSocket for TerminalWriter<Inner> {
101 #[inline]
102 fn as_handle_or_socket(&self) -> BorrowedHandleOrSocket<'_> {
103 self.inner.as_handle_or_socket()
104 }
105}
106
107impl<Inner: Write> Terminal for TerminalWriter<Inner> {}
108
109impl<Inner: Write> WriteTerminal for TerminalWriter<Inner> {
110 fn color_support(&self) -> TerminalColorSupport {
111 self.write_config
112 .as_ref()
113 .map_or_else(Default::default, |c| c.color_support)
114 }
115
116 fn color_preference(&self) -> bool {
117 self.write_config
118 .as_ref()
119 .map_or(false, |c| c.color_preference)
120 }
121
122 fn is_output_terminal(&self) -> bool {
123 self.write_config.is_some()
124 }
125}
126
127impl<Inner: Write> Write for TerminalWriter<Inner> {
128 #[inline]
129 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
130 self.inner.write(buf)
131 }
132
133 #[inline]
134 fn flush(&mut self) -> io::Result<()> {
135 self.inner.flush()
136 }
137
138 #[inline]
139 fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result<usize> {
140 self.inner.write_vectored(bufs)
141 }
142
143 #[cfg(can_vector)]
144 #[inline]
145 fn is_write_vectored(&self) -> bool {
146 self.inner.is_write_vectored()
147 }
148
149 #[inline]
150 fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
151 self.inner.write_all(buf)
152 }
153
154 #[cfg(write_all_vectored)]
155 #[inline]
156 fn write_all_vectored(&mut self, bufs: &mut [IoSlice]) -> io::Result<()> {
157 self.inner.write_all_vectored(bufs)
158 }
159
160 #[inline]
161 fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> {
162 self.inner.write_fmt(fmt)
163 }
164}