Skip to main content

Process

Struct Process 

Source
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

Source

pub fn new(program: impl Into<OsString>) -> Self

A child process that runs program, with pipes and the application’s own environment.

Source

pub fn arg(self, arg: impl Into<OsString>) -> Self

Adds one argument.

Source

pub fn args(self, args: impl IntoIterator<Item = impl Into<OsString>>) -> Self

Adds several arguments, in order.

Source

pub fn dir(self, dir: impl Into<PathBuf>) -> Self

Runs the child in dir instead of the application’s working directory.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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§

Source§

impl Clone for Process

Source§

fn clone(&self) -> Process

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Process

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Eq for Process

Source§

impl PartialEq for Process

Source§

fn eq(&self, other: &Process) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for Process

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.