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())]);

For output that is not read line by line, Process::collect keeps only the end of it.

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 clear_env(self) -> Self

Gives the child nothing of the application’s environment: what it gets is what was set with Process::env and nothing else, so a program sees what the application said it would see rather than what the shell the application was started from happened to have.

PATH is one of the variables that has to be named, since it is what the child looks other programs up with, as in the example. HOME, LANG and the rest are just as gone.

use qframe::runtime::{Line, Process, ProcessOutcome};

let path = std::env::var("PATH").expect("a path to look other programs up with");
let mut lines = Vec::new();
let outcome = Process::new("sh")
    .args(["-c", "printf 'sadece bu'"])
    .clear_env()
    .env("PATH", path)
    .run(&|| false, &mut |line| lines.push(line))?;
assert_eq!(lines, vec![Line::Out("sadece bu".to_owned())]);
assert_eq!(outcome, ProcessOutcome::Finished { code: Some(0) });

Process::env still sets a variable here, but that is now all the child gets.

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.

Source

pub fn collect(self, keep: Keep, cancel: &dyn Fn() -> bool) -> Result<Collected>

Runs the child, keeping only the end of what it writes, and gives that back as one piece of text.

Both of the child’s streams land on one pipe, so the text is in the order the child wrote it and a failure stays among the rest of the output; with Process::pty both land on the terminal itself, as they always do there. Nothing is delivered line by line: the point is to hold the end of the output, so a program that prints megabytes, a build’s whole log, a flood of progress, costs no more memory than Keep asks for however long it writes.

Keep::limit ends the child the way cancel does, its process group too where Process::no_stdin asked for one, and both give back the output kept so far in bounded time.

Meant to be called inside a Task, with cancel reading TaskCx::is_cancelled.

use std::time::Duration;
use qframe::runtime::{Keep, Process, ProcessOutcome};

let collected = Process::new("sh")
    .args(["-c", "echo ready"])
    .env("LC_ALL", "C")
    .collect(Keep::bytes(4096).lines(20).limit(Duration::from_secs(30)), &|| false)?;
assert_eq!(collected.text, "ready\n");
assert_eq!(collected.outcome, ProcessOutcome::Finished { code: Some(0) });
§Errors

The same as Process::run.

Trait Implementations§

Source§

impl Clone for Process

Source§

fn clone(&self) -> Self

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: &Self) -> 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.