pub struct Process { /* private fields */ }Expand description
A child process whose output is read line by line, for showing in a log view.
use qframe::runtime::{Line, Process};
let mut lines = Vec::new();
let outcome = Process::new("sh")
.args(["-c", "echo ready"])
.env("LC_ALL", "C")
.run(&|| false, &mut |line| lines.push(line))?;
assert_eq!(lines, vec![Line::Out("ready".to_owned())]);Implementations§
Source§impl Process
impl Process
Sourcepub fn new(program: impl Into<OsString>) -> Self
pub fn new(program: impl Into<OsString>) -> Self
A child process that runs program, with pipes and the application’s own environment.
Sourcepub fn args(self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Self
pub fn args(self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Self
Adds several arguments, in order.
Sourcepub fn dir(self, dir: impl Into<PathBuf>) -> Self
pub fn dir(self, dir: impl Into<PathBuf>) -> Self
Runs the child in dir instead of the application’s working directory.
Sourcepub fn env(self, key: impl Into<OsString>, value: impl Into<OsString>) -> Self
pub fn env(self, key: impl Into<OsString>, value: impl Into<OsString>) -> Self
Sets one environment variable for the child. The rest of the environment is inherited, and setting a variable the application already has replaces it for the child only.
Sourcepub fn pty(self, cols: u16, rows: u16) -> Self
pub fn pty(self, cols: u16, rows: u16) -> Self
Runs the child on a pseudo-terminal cols wide and rows tall, so programs that check
for a terminal draw their progress and colour. Standard input stays the application’s
own unless Process::no_stdin is asked for, and the child keeps the controlling
terminal, which is what keeps a warm sudo ticket shared. Without this the child gets
pipes and sees no terminal.
The child reads the size given here, not the real terminal’s, so its progress bar fits the space the application is going to draw it in.
Sourcepub fn no_stdin(self) -> Self
pub fn no_stdin(self) -> Self
Gives the child no standard input: it reads an empty stream (/dev/null) instead of the
application’s terminal. A program that asks a question then gets no answer rather than
the keys meant for the application, which it would otherwise take from under it.
On Unix the child also starts in a process group of its own, so cancelling ends the
programs it started as well; see Process::run. It keeps the application’s session
and controlling terminal, so a warm sudo ticket still applies. A program that reads
the terminal itself anyway, as sudo does to ask for a password, is stopped by the
system until it is cancelled, because its group is not the one the terminal belongs to:
warm the ticket first with a Handoff of sudo -v, or pass
sudo -n so it fails at once instead of asking.
Sourcepub fn run(
self,
cancel: &dyn Fn() -> bool,
on_line: &mut dyn FnMut(Line),
) -> Result<ProcessOutcome>
pub fn run( self, cancel: &dyn Fn() -> bool, on_line: &mut dyn FnMut(Line), ) -> Result<ProcessOutcome>
Runs the child, handing every line to on_line, and returns how it ended.
Lines arrive one by one, without their newline. A \r overwrites the line being built
rather than starting a new one, which is how progress bars are written, and the last
line is delivered even when the output does not end with a newline. Bytes that are not
UTF-8 become the replacement character instead of being dropped. A line longer than
64 KiB is delivered in pieces of at most that size, cut between characters, so a program
that never writes a newline cannot make the reader hold all of its output.
When the child writes faster than on_line takes its lines, the reading waits and the
child waits with it, rather than its output piling up in memory.
cancel is asked between lines, and every few milliseconds while there is none, also
after the child has closed its output but keeps running; when it turns true the child is
killed, its pending output is dropped and the outcome is ProcessOutcome::Cancelled.
What cancelling kills depends on standard input. With Process::no_stdin on Unix, the
child runs in a process group of its own and the whole group is killed, so the programs
it started go with it (podman with buildah and the build’s steps), except those that
moved to a group or session of their own. Without it the child shares the application’s
standard input, which is the terminal: in a group of its own it would be stopped by the
system the first time it read from it, so it stays in the application’s group and only
the child itself is killed; a program that started children of its own can leave them
running.
Meant to be called inside a Task, with cancel reading
TaskCx::is_cancelled.
§Errors
Returns an I/O error when the child cannot be started, when a pseudo-terminal was asked for and cannot be opened, or when a reading thread cannot be started.
Sourcepub fn run_with_overwritten(
self,
cancel: &dyn Fn() -> bool,
on_line: &mut dyn FnMut(Line),
on_overwritten: &mut dyn FnMut(Line),
) -> Result<ProcessOutcome>
pub fn run_with_overwritten( self, cancel: &dyn Fn() -> bool, on_line: &mut dyn FnMut(Line), on_overwritten: &mut dyn FnMut(Line), ) -> Result<ProcessOutcome>
Runs the child like Process::run, and also hands every line a \r overwrites to
on_overwritten instead of dropping it: the frames of a progress bar, as cargo,
pacman, curl and git write them.
A frame is the text built since the last line end or \r, delivered when the byte
after the \r shows that the line really is overwritten; \r\n and \r\r\n stay
plain line ends and give no frame, and an empty frame is not delivered. When the output
ends right after a \r, its last frame is delivered too. Colour codes and erase codes
such as ESC [K are passed on untouched. A frame comes tagged like a line: Line::Out
or Line::Err for the stream it was written to, and always Line::Out on a
pseudo-terminal. Lines and frames arrive in the order the child wrote them; on_line
receives exactly what Process::run would hand it.
use qframe::runtime::{Line, Process};
let (mut lines, mut frames) = (Vec::new(), Vec::new());
Process::new("sh").args(["-c", r"printf '10%\r50%\rdone\n'"]).run_with_overwritten(
&|| false,
&mut |line| lines.push(line),
&mut |frame| frames.push(frame),
)?;
assert_eq!(lines, vec![Line::Out("done".to_owned())]);
assert_eq!(frames, vec![Line::Out("10%".to_owned()), Line::Out("50%".to_owned())]);§Errors
The same as Process::run.
Trait Implementations§
impl Eq for Process
impl StructuralPartialEq for Process
Auto Trait Implementations§
impl Freeze for Process
impl RefUnwindSafe for Process
impl Send for Process
impl Sync for Process
impl Unpin for Process
impl UnsafeUnpin for Process
impl UnwindSafe for Process
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more