prettytty/opt.rs
1//! Helper module with the options for connecting to terminals.
2//!
3//! This module provides the options for a terminal connection and the
4//! corresponding builder.
5//!
6//!
7//! # Example
8//!
9//! ```
10//! # use prettytty::opt::Options;
11//! let options = Options::builder()
12//! .timeout(50)
13//! .build();
14//!
15//! assert_eq!(options.timeout(), 50);
16//! ```
17
18/// The diagnostic logging volume.
19#[derive(Clone, Copy, Debug, PartialEq, Eq)]
20pub enum Volume {
21 Silent,
22 Regular,
23 Detailed,
24}
25
26/// A terminal mode.
27///
28/// Currently four terminal modes are supported:
29///
30/// * __Charred mode__ considers the terminal configuration as too hot to
31/// touch and makes no changes.
32///
33/// * __Cooked mode__ is the usual mode of operation on Unix and includes
34/// several features that go beyond character-based I/O, including editing
35/// the input line by line, turning key presses such as control-c into
36/// signals, and translating line endings.
37///
38/// On Windows, this mode optimizes for interoperability, enables the UTF-8
39/// code page for input and output, while also activating
40/// `ENABLE_VIRTUAL_TERMINAL_INPUT`, `ENABLE_PROCESSED_OUTPUT`, and
41/// `ENABLE_VIRTUAL_TERMINAL_PROCESSING`.
42///
43/// * __Rare mode__, also called cbreak mode, disables the line editor but
44/// leaves other terminal convenience features such as processing control-c
45/// enabled. This is the default mode for prettytty.
46///
47/// * __Raw mode__ disables all features beyond character-based I/O and ANSI
48/// escape sequences. It maximizes the application's control over input and
49/// output, but it also places the burden of implementing features at least as
50/// good as line editing on the application developer.
51///
52#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
53pub enum Mode {
54 /// Charred mode doesn't dare to touch the terminal configuration; it's too
55 /// hot.
56 Charred,
57 /// Cooked mode means turning control-c/d into signals, fiddling with
58 /// line-endings in the output, and always editing the input line by line.
59 /// Still, it allows for ANSI escape sequences.
60 Cooked,
61 /// Rare or cbreak mode.
62 #[default]
63 Rare,
64 /// Raw mode.
65 Raw,
66}
67
68#[derive(Clone, Debug)]
69struct OptionData {
70 volume: Volume,
71 mode: Mode,
72 timeout: u8,
73 pathological_size: usize,
74 read_buffer_size: usize,
75 write_buffer_size: usize,
76}
77
78impl OptionData {
79 pub const fn new() -> Self {
80 Self {
81 volume: Volume::Silent,
82 mode: Mode::Rare,
83 timeout: 10,
84 pathological_size: 512,
85 read_buffer_size: 256,
86 write_buffer_size: 1_024,
87 }
88 }
89}
90
91/// A builder of options objects.
92#[derive(Debug)]
93pub struct OptionBuilder(OptionData);
94
95impl OptionBuilder {
96 /// Set the volume.
97 pub fn volume(&mut self, volume: Volume) -> &mut Self {
98 self.0.volume = volume;
99 self
100 }
101
102 /// Set rare or raw mode.
103 pub fn mode(&mut self, mode: Mode) -> &mut Self {
104 self.0.mode = mode;
105 self
106 }
107
108 /// Set the timeout in deciseconds (0.1s).
109 pub fn timeout(&mut self, timeout: u8) -> &mut Self {
110 self.0.timeout = timeout;
111 self
112 }
113
114 /// Set the minimum length for pathological ANSI escape sequences.
115 ///
116 /// This method ensures that the given size is at least double the read
117 /// buffer size, updating it if necessary.
118 pub fn pathological_size(&mut self, size: usize) -> &mut Self {
119 self.0.pathological_size = size.max(
120 self.0
121 .read_buffer_size
122 .saturating_add(self.0.read_buffer_size),
123 );
124 self
125 }
126
127 /// Set the read buffer size.
128 ///
129 /// This method also updates the pathological size to twice the given size.
130 ///
131 /// The read buffer must be large enough to hold the entire escape sequence
132 /// being recognized. When querying colors, that is 27 bytes: A response for
133 /// the 16th ANSI color *bright white* starts with `‹OSC›4;15;rgb:` followed
134 /// by three four-digit hexadecimal numbers separated by forward slashes,
135 /// such as `ffff/ffff/ffff`, and then the terminating `‹ST›`. Both OSC and
136 /// ST require at most two bytes, resulting in a maximum sequence length of
137 /// 27 bytes.
138 ///
139 /// This method ensures that the pathological size is at least double the
140 /// given size, updating it if necessary.
141 pub fn read_buffer_size(&mut self, size: usize) -> &mut Self {
142 self.0.read_buffer_size = size;
143 self.0.pathological_size = self.0.pathological_size.max(size.saturating_add(size));
144 self
145 }
146
147 /// Set the write buffer size.
148 pub fn write_buffer_size(&mut self, size: usize) -> &mut Self {
149 self.0.write_buffer_size = size;
150 self
151 }
152
153 /// Instantiate the options.
154 #[must_use = "the only reason to invoke method is to access the returned value"]
155 pub fn build(&self) -> Options {
156 Options(self.0.clone())
157 }
158}
159
160/// An options object.
161#[derive(Debug)]
162pub struct Options(OptionData);
163
164impl Default for Options {
165 fn default() -> Self {
166 Self(OptionData::new())
167 }
168}
169
170impl Options {
171 /// Create a new builder with the default option values.
172 pub fn builder() -> OptionBuilder {
173 OptionBuilder(OptionData::new())
174 }
175
176 /// Instantiate the default options but with regular debugging output
177 /// enabled.
178 pub fn with_log() -> Self {
179 Self::builder().volume(Volume::Regular).build()
180 }
181
182 /// Instantiate the default options but with detailed debugging output
183 /// enabled.
184 pub fn with_detailed_log() -> Self {
185 Self::builder().volume(Volume::Detailed).build()
186 }
187
188 /// Get the volume.
189 pub fn volume(&self) -> Volume {
190 self.0.volume
191 }
192
193 /// Get the terminal mode.
194 pub fn mode(&self) -> Mode {
195 self.0.mode
196 }
197
198 /// Get the timeout in 0.1s increments for blocking read operations.
199 pub fn timeout(&self) -> u8 {
200 self.0.timeout
201 }
202
203 /// Get the pathological size.
204 pub fn pathological_size(&self) -> usize {
205 self.0.pathological_size
206 }
207
208 /// Get the size of the read buffer.
209 pub fn read_buffer_size(&self) -> usize {
210 self.0.read_buffer_size
211 }
212
213 /// Get the size of the write buffer.
214 pub fn write_buffer_size(&self) -> usize {
215 self.0.write_buffer_size
216 }
217}