[![Maintenance](https://img.shields.io/badge/maintenance-unreleased-critical?style=for-the-badge)]() [![License](https://img.shields.io/badge/license-MIT-informational?style=for-the-badge)](./LICENSE.md) [![Coverage](https://img.shields.io/gitlab/coverage/detly/calloop-subproc/main?style=for-the-badge)]() [![Rust](https://img.shields.io/badge/rust-^1.56-informational?style=for-the-badge)]() # calloop-subprocess Subprocess handling event source for the [Calloop event loop][calloop]. [calloop]: https://crates.io/crates/calloop Calloop is built around the concept of *event sources* eg. timers, channels, file descriptors themselves, which generate events and call a callback for them. This crate provides two kinds of event sources that let you manage subprocess: - a [chain], which runs a series of commands and generates a success/failure event - a [listener], which runs a single command and generates an event based on lines of output [chain]: crate::SubprocChain [listener]: crate::SubprocListen # Error handling Errors in this crate are classified into two high-level kinds, as is the [custom for Calloop][calloop-errors]. [calloop-errors]: https://smithay.github.io/calloop/ch02-06-errors.html [`LaunchError`] might be generated by an event source's [`process_events()`] method when a critical error is encountered. This generally means that the event source itself can't continue sensibly and should be removed from the event loop. The [`ErrorEvent`] type is concerned with errors of the subprocess it's managing, including the command not being found or requiring permissions that the process doesn't have. These will be contained in the type of event that is given to the callback. The main way that these are different is: if there's a problem using whatever underlying mechanism we use to spawn a subprocess or get results back from it, then a [`LaunchError`] is produced then and there. But if the underlying mechanics for setting up a subprocess succeed, and it's just that the command contains a typo and can't be found, then it's perfectly possible to generate an event for that. It will just happen to contain the [IO error] you'd expect when trying to run the command directly. [IO error]: std::io::Error # Releasing file descriptors To avoid "holding on" to file descriptors (and just the memory associated with the event source itself), you **must** honour the [`calloop::PostAction`] returned from [`process_events()`] and remove the event source if requested. This is automatically done for top level event sources ie. those added by [`insert_source()`]. If you use it as part of a composed event source, you must either manage reregistration yourself, or wrap the source with [`TransientSource`](calloop::transient::TransientSource). [`insert_source()`]: calloop::LoopHandle::insert_source() # Example This runs `ls -a` and prints the events received. ```rust use calloop::{EventLoop, LoopSignal}; use calloop_subproc::{Command, SubprocListen}; let ls_cmd = Command::new("ls").with_args(["-a"]); let listener = SubprocListen::new(ls_cmd).unwrap(); let mut event_loop: EventLoop = EventLoop::try_new().unwrap(); event_loop .handle() .insert_source(listener, |event, _, stopper| { // What kind of event did we get this time? let msg = match event { // The subprocess was just started. calloop_subproc::ListenEvent::Start => "Subprocess started".to_owned(), // We got a line of output from the subprocess. calloop_subproc::ListenEvent::Line(line) => format!("Output: {}", line), // The subprocess ended. calloop_subproc::ListenEvent::End(res) => { // Since the subprocess ended, we want to stop the loop. stopper.stop(); // Show why the subprocess ended. match res { Ok(()) => "Subprocess completed".to_owned(), Err(error) => format!("Subprocess error: {:?}", error), } } }; // Print our formatted event. println!("{}", msg); // This callback must return true if the subprocess should be // killed. We want it to run to completion, so we return false. false }) .unwrap(); event_loop .run(None, &mut event_loop.get_signal(), |_| {}) .unwrap(); ``` [`process_events()`]: calloop::EventSource::process_events --- This document is kept up-to-date with [cargo-rdme](https://github.com/orium/cargo-rdme).