Skip to content

Commit 59b8286

Browse files
authored
Merge pull request #166 from epage/cleanup
docs: Clean up the documentation
2 parents a49324a + beda4a1 commit 59b8286

File tree

3 files changed

+201
-172
lines changed

3 files changed

+201
-172
lines changed

src/process.rs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use std::os::unix::process::CommandExt;
1818
use std::process::Command;
1919
use std::{thread, time};
2020

21-
/// Start a process in a forked tty so you can interact with it the same as you would
21+
/// Start a process in a forked tty to interact with it like you would
2222
/// within a terminal
2323
///
2424
/// The process and pty session are killed upon dropping `PtyProcess`
@@ -144,17 +144,18 @@ impl PtyProcess {
144144
Ok(fd.into())
145145
}
146146

147-
/// At the drop of `PtyProcess` the running process is killed. This is blocking forever if
148-
/// the process does not react to a normal kill. If `kill_timeout` is set the process is
149-
/// `kill -9`ed after duration
147+
/// At the drop of `PtyProcess` the running process is killed (blocking).
148+
///
149+
/// This is blocking forever if the process does not react to a normal kill.
150+
/// If `kill_timeout` is set the process is `kill -9`ed after duration.
150151
pub fn set_kill_timeout(&mut self, timeout_ms: Option<u64>) {
151152
self.kill_timeout = timeout_ms.map(time::Duration::from_millis);
152153
}
153154

154-
/// Get status of child process, non-blocking.
155+
/// Get status of child process (non-blocking).
155156
///
156-
/// This method runs waitpid on the process.
157-
/// This means: If you ran `exit()` before or `status()` this method will
157+
/// This method runs waitpid on the process:
158+
/// if you ran `exit()` before or `status()` this method will
158159
/// return `None`
159160
///
160161
/// # Example
@@ -176,29 +177,34 @@ impl PtyProcess {
176177
wait::waitpid(self.child_pid, Some(wait::WaitPidFlag::WNOHANG)).ok()
177178
}
178179

179-
/// Wait until process has exited. This is a blocking call.
180+
/// Wait until process has exited (non-blocking).
181+
///
180182
/// If the process doesn't terminate this will block forever.
181183
pub fn wait(&self) -> Result<wait::WaitStatus, Error> {
182184
wait::waitpid(self.child_pid, None).map_err(Error::from)
183185
}
184186

185-
/// Regularly exit the process, this method is blocking until the process is dead
187+
/// Regularly exit the process (blocking).
188+
///
189+
/// This method is blocking until the process is dead
186190
pub fn exit(&mut self) -> Result<wait::WaitStatus, Error> {
187191
self.kill(signal::SIGTERM)
188192
}
189193

190-
/// Non-blocking variant of `kill()` (doesn't wait for process to be killed)
194+
/// Kill the process with a specific signal (non-blocking).
191195
pub fn signal(&mut self, sig: signal::Signal) -> Result<(), Error> {
192196
signal::kill(self.child_pid, sig).map_err(Error::from)
193197
}
194198

195-
/// Kill the process with a specific signal. This method blocks, until the process is dead
199+
/// Kill the process with a specific signal (blocking).
200+
///
201+
/// This method blocks until the process is dead
196202
///
197-
/// repeatedly sends SIGTERM to the process until it died,
203+
/// This repeatedly sends SIGTERM to the process until it died,
198204
/// the pty session is closed upon dropping `PtyMaster`,
199205
/// so we don't need to explicitly do that here.
200206
///
201-
/// if `kill_timeout` is set and a repeated sending of signal does not result in the process
207+
/// If `kill_timeout` is set and a repeated sending of signal does not result in the process
202208
/// being killed, then `kill -9` is sent after the `kill_timeout` duration has elapsed.
203209
pub fn kill(&mut self, sig: signal::Signal) -> Result<wait::WaitStatus, Error> {
204210
let start = time::Instant::now();

src/reader.rs

Lines changed: 122 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -8,105 +8,14 @@ use std::sync::mpsc::{Receiver, channel};
88
use std::thread;
99
use std::{fmt, time};
1010

11-
#[derive(Debug)]
12-
enum PipeError {
13-
IO(io::Error),
14-
}
15-
16-
#[derive(Debug)]
17-
#[allow(clippy::upper_case_acronyms)]
18-
enum PipedChar {
19-
Char(u8),
20-
EOF,
21-
}
22-
23-
pub enum ReadUntil {
24-
String(String),
25-
Regex(Regex),
26-
EOF,
27-
NBytes(usize),
28-
Any(Vec<ReadUntil>),
29-
}
30-
31-
impl fmt::Display for ReadUntil {
32-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
33-
let printable = match self {
34-
ReadUntil::String(s) if s == "\n" => "\\n (newline)".to_owned(),
35-
ReadUntil::String(s) if s == "\r" => "\\r (carriage return)".to_owned(),
36-
ReadUntil::String(s) => format!("\"{s}\""),
37-
ReadUntil::Regex(r) => format!("Regex: \"{r}\""),
38-
ReadUntil::EOF => "EOF (End of File)".to_owned(),
39-
ReadUntil::NBytes(n) => format!("reading {n} bytes"),
40-
ReadUntil::Any(v) => {
41-
let mut res = Vec::new();
42-
for r in v {
43-
res.push(r.to_string());
44-
}
45-
res.join(", ")
46-
}
47-
};
48-
write!(f, "{printable}")
49-
}
50-
}
51-
52-
/// find first occurrence of needle within buffer
53-
///
54-
/// # Arguments:
55-
///
56-
/// - buffer: the currently read buffer from a process which will still grow in the future
57-
/// - eof: if the process already sent an EOF or a HUP
58-
///
59-
/// # Return
60-
///
61-
/// Tuple with match positions:
62-
/// 1. position before match (0 in case of EOF and Nbytes)
63-
/// 2. position after match
64-
pub fn find(needle: &ReadUntil, buffer: &str, eof: bool) -> Option<(usize, usize)> {
65-
match needle {
66-
ReadUntil::String(s) => buffer.find(s).map(|pos| (pos, pos + s.len())),
67-
ReadUntil::Regex(pattern) => pattern.find(buffer).map(|mat| (mat.start(), mat.end())),
68-
ReadUntil::EOF => {
69-
if eof {
70-
Some((0, buffer.len()))
71-
} else {
72-
None
73-
}
74-
}
75-
ReadUntil::NBytes(n) => {
76-
if *n <= buffer.len() {
77-
Some((0, *n))
78-
} else if eof && !buffer.is_empty() {
79-
// reached almost end of buffer, return string, even though it will be
80-
// smaller than the wished n bytes
81-
Some((0, buffer.len()))
82-
} else {
83-
None
84-
}
85-
}
86-
ReadUntil::Any(anys) => anys
87-
.iter()
88-
// Filter matching needles
89-
.filter_map(|any| find(any, buffer, eof))
90-
// Return the left-most match
91-
.min_by(|(start1, end1), (start2, end2)| {
92-
if start1 == start2 {
93-
end1.cmp(end2)
94-
} else {
95-
start1.cmp(start2)
96-
}
97-
}),
98-
}
99-
}
100-
101-
/// Options for `NBReader`
102-
///
103-
/// - timeout:
104-
/// + `None`: `read_until` is blocking forever. This is probably not what you want
105-
/// + `Some(millis)`: after millis milliseconds a timeout error is raised
106-
/// - `strip_ansi_escape_codes`: Whether to filter out escape codes, such as colors.
11+
/// Options for [`NBReader`]
10712
#[derive(Default)]
10813
pub struct Options {
14+
/// `None`: `read_until` is blocking forever. This is probably not what you want
15+
///
16+
/// `Some(millis)`: after millis milliseconds a timeout error is raised
10917
pub timeout_ms: Option<u64>,
18+
/// Whether to filter out escape codes, such as colors.
11019
pub strip_ansi_escape_codes: bool,
11120
}
11221

@@ -125,10 +34,10 @@ pub struct NBReader {
12534
impl NBReader {
12635
/// Create a new reader instance
12736
///
128-
/// # Arguments:
37+
/// # Arguments
12938
///
130-
/// - f: file like object
131-
/// - options: see `Options`
39+
/// - `f`: file like object
40+
/// - `options`: see [`Options`]
13241
pub fn new<R: Read + Send + 'static>(f: R, options: Options) -> NBReader {
13342
let (tx, rx) = channel();
13443

@@ -177,7 +86,7 @@ impl NBReader {
17786
}
17887
}
17988

180-
/// reads all available chars from the read channel and stores them in self.buffer
89+
/// Reads all available chars from the read channel and stores them in [`Self::buffer`]
18190
fn read_into_buffer(&mut self) -> Result<(), Error> {
18291
if self.eof {
18392
return Ok(());
@@ -199,24 +108,13 @@ impl NBReader {
199108
Ok(())
200109
}
201110

202-
/// Read until needle is found (blocking!) and return tuple with:
203-
/// 1. yet unread string until and without needle
204-
/// 2. matched needle
111+
/// Read until needle is found (blocking!)
205112
///
206113
/// This methods loops (while reading from the Cursor) until the needle is found.
207114
///
208-
/// There are different modes:
209-
///
210-
/// - `ReadUntil::String` searches for string (use '\n'.`to_string()` to search for newline).
211-
/// Returns not yet read data in first String, and needle in second String
212-
/// - `ReadUntil::Regex` searches for regex
213-
/// Returns not yet read data in first String and matched regex in second String
214-
/// - `ReadUntil::NBytes` reads maximum n bytes
215-
/// Returns n bytes in second String, first String is left empty
216-
/// - `ReadUntil::EOF` reads until end of file is reached
217-
/// Returns all bytes in second String, first is left empty
218-
///
219-
/// Note that when used with a tty the lines end with \r\n
115+
/// Returns a tuple with:
116+
/// 1. yet unread string until and without needle
117+
/// 2. matched needle
220118
///
221119
/// Returns error if EOF is reached before the needle could be found.
222120
///
@@ -283,8 +181,9 @@ impl NBReader {
283181
}
284182
}
285183

286-
/// Try to read one char from internal buffer. Returns None if
287-
/// no char is ready, Some(char) otherwise. This is non-blocking
184+
/// Try to read one char from internal buffer (non-blocking).
185+
///
186+
/// Returns `None` if no char is ready `Some(char)` otherwise.
288187
pub fn try_read(&mut self) -> Option<char> {
289188
// discard eventual errors, EOF will be handled in read_until correctly
290189
let _ = self.read_into_buffer();
@@ -296,6 +195,112 @@ impl NBReader {
296195
}
297196
}
298197

198+
/// See [`NBReader::read_until`]
199+
///
200+
/// Note that when used with a tty the lines end with \r\n
201+
pub enum ReadUntil {
202+
/// Searches for string (use '\n'.`to_string()` to search for newline).
203+
///
204+
/// Returns not yet read data in first String, and needle in second String
205+
String(String),
206+
/// `ReadUntil::Regex` searches for regex
207+
///
208+
/// Returns not yet read data in first String and matched regex in second String
209+
Regex(Regex),
210+
/// `ReadUntil::NBytes` reads maximum n bytes
211+
///
212+
/// Returns n bytes in second String, first String is left empty
213+
NBytes(usize),
214+
/// `ReadUntil::EOF` reads until end of file is reached
215+
///
216+
/// Returns all bytes in second String, first is left empty
217+
EOF,
218+
Any(Vec<ReadUntil>),
219+
}
220+
221+
impl fmt::Display for ReadUntil {
222+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223+
match self {
224+
ReadUntil::String(s) if s == "\n" => write!(f, "\\n (newline)"),
225+
ReadUntil::String(s) if s == "\r" => write!(f, "\\r (carriage return)"),
226+
ReadUntil::String(s) => write!(f, "\"{s}\""),
227+
ReadUntil::Regex(r) => write!(f, "Regex: \"{r}\""),
228+
ReadUntil::NBytes(n) => write!(f, "reading {n} bytes"),
229+
ReadUntil::EOF => write!(f, "EOF (End of File)"),
230+
ReadUntil::Any(v) => {
231+
for (i, r) in v.iter().enumerate() {
232+
if i != 0 {
233+
write!(f, ", ")?;
234+
}
235+
write!(f, "{r}")?;
236+
}
237+
Ok(())
238+
}
239+
}
240+
}
241+
}
242+
243+
/// Find first occurrence of needle within buffer
244+
///
245+
/// # Arguments:
246+
///
247+
/// - `buffer`: the currently read buffer from a process which will still grow in the future
248+
/// - `eof`: if the process already sent an EOF or a HUP
249+
///
250+
/// # Return
251+
///
252+
/// Tuple with match positions:
253+
/// 1. position before match (0 in case of EOF and Nbytes)
254+
/// 2. position after match
255+
pub fn find(needle: &ReadUntil, buffer: &str, eof: bool) -> Option<(usize, usize)> {
256+
match needle {
257+
ReadUntil::String(s) => buffer.find(s).map(|pos| (pos, pos + s.len())),
258+
ReadUntil::Regex(pattern) => pattern.find(buffer).map(|mat| (mat.start(), mat.end())),
259+
ReadUntil::EOF => {
260+
if eof {
261+
Some((0, buffer.len()))
262+
} else {
263+
None
264+
}
265+
}
266+
ReadUntil::NBytes(n) => {
267+
if *n <= buffer.len() {
268+
Some((0, *n))
269+
} else if eof && !buffer.is_empty() {
270+
// reached almost end of buffer, return string, even though it will be
271+
// smaller than the wished n bytes
272+
Some((0, buffer.len()))
273+
} else {
274+
None
275+
}
276+
}
277+
ReadUntil::Any(anys) => anys
278+
.iter()
279+
// Filter matching needles
280+
.filter_map(|any| find(any, buffer, eof))
281+
// Return the left-most match
282+
.min_by(|(start1, end1), (start2, end2)| {
283+
if start1 == start2 {
284+
end1.cmp(end2)
285+
} else {
286+
start1.cmp(start2)
287+
}
288+
}),
289+
}
290+
}
291+
292+
#[derive(Debug)]
293+
enum PipeError {
294+
IO(io::Error),
295+
}
296+
297+
#[derive(Debug)]
298+
#[allow(clippy::upper_case_acronyms)]
299+
enum PipedChar {
300+
Char(u8),
301+
EOF,
302+
}
303+
299304
#[cfg(test)]
300305
mod tests {
301306
use super::*;

0 commit comments

Comments
 (0)