Set of Rust libraries to manage processes.
Designing a generic API for a library that relies on I/O is a real challenge. This project tries to solve that matter by abstracting away the I/O:
- The core lib exposes I/O-free, composable and iterable state machines (named flows)
- The I/O connector executes I/O everytime a flow
Iterator
produces an I/O request
let mut flow = Flow::new()
let conn = IoConnector::new();
while let Some(io) = flow.next() {
conn.execute(&mut flow, io)?;
}
let output = flow.output()
✔ If you develop a library that relies on processes, then process-lib
might help you.
✔ If you develop an application that uses process-lib
flows, any I/O connector might help you. If you do not find the right built-in connector matching your needs, you can easily build your own. PRs are welcomed!
✘ If you develop an application without process-lib
, then this project might not help you. Just use directly the process module matching your environment (std::process
, tokio::process
etc).
process-lib
: a set of I/O-free tools to deal with processesprocess-std
: a standard, blocking I/O connector forprocess-lib
process-tokio
: Tokio-based, async I/O connector forprocess-lib
use process_lib::{Command, SpawnCommandThenWait};
use process_std::Connector;
fn main() {
let conn = Connector::new();
let mut command = Command::new("echo");
command.arg("hello");
command.arg("world");
let mut flow = SpawnCommandThenWait::new(command);
while let Some(io) = flow.next() {
conn.execute(&mut flow, io).unwrap();
}
let status = flow.take_status().unwrap();
println!("status: {status:#?}");
// status: ExitStatus(
// unix_wait_status(
// 0,
// ),
// )
}
use process_lib::{Command, SpawnCommandThenWaitWithOutput};
use process_tokio::Connector;
fn main() {
let conn = Connector::new();
let mut command = Command::new("echo");
command.arg("hello");
command.arg("world");
let mut flow = SpawnCommandThenWaitWithOutput::new(command);
while let Some(io) = flow.next() {
conn.execute(&mut flow, io).await.unwrap();
}
let output = flow.take_output().unwrap();
println!("output: {output:#?}");
// output: Output {
// status: ExitStatus(
// unix_wait_status(
// 0,
// ),
// ),
// stdout: "hello world\n",
// stderr: "",
// }
}
use std::process::Stdio;
use process_lib::{Command, SpawnCommandThenWait, SpawnCommandThenWaitWithOutput};
use process_std::Connector;
fn main() {
let conn = Connector::new();
let mut command = Command::new("echo");
command.arg("hello");
command.arg("world");
command.stdout(Stdio::piped());
let mut flow = SpawnCommandThenWait::new(command);
while let Some(io) = flow.next() {
conn.execute(&mut flow, io).unwrap();
}
let stdout = flow.take_stdout().unwrap();
// move stdout from previous command to next command stdin
let mut command = Command::new("cat");
command.arg("-E");
command.stdin(stdout);
let mut flow = SpawnCommandThenWaitWithOutput::new(command);
while let Some(io) = flow.next() {
conn.execute(&mut flow, io).unwrap();
}
let output = flow.take_output().unwrap();
println!("output: {output:#?}");
// output: Output {
// status: ExitStatus(
// unix_wait_status(
// 0,
// ),
// ),
// stdout: "hello world$\n",
// stderr: "",
// }
}
See more examples at process-std/examples
and process-tokio/examples
.
Special thanks to the NLnet foundation and the European Commission that helped the project to receive financial support from various programs:
- NGI Assure in 2022
- NGI Zero Entrust in 2023
- NGI Zero Core in 2024 (still ongoing)
If you appreciate the project, feel free to donate using one of the following providers: