proc_heim/lib.rs
1#![cfg_attr(all(doc, CHANNEL_NIGHTLY), feature(doc_auto_cfg))]
2
3//! # Proc-heim
4//!
5//! `Proc-heim` is a library for running and managing short-lived and long-lived processes using asynchronous API. A new process can be created by running either command or script.
6//!
7//! ## Features
8//! `Proc-heim` internally uses [`tokio::process`](https://docs.rs/tokio/latest/tokio/process/index.html) for executing processes and provides all its functionality plus additional features:
9//! * spawning new processes via scripts (in different scripting languages) and any `Rust` types, which implements `Runnable` trait,
10//! * flexible managing of all spawned processes using single facade, which can be easily shared by multiple threads/tasks,
11//! * bi-directional, message-based communication between child and parent processes via standard IO streams or named pipes,
12//! * collecting and querying logs produced by child processes (both running and completed).
13//!
14//! For more detailed list of features see [`ProcessManagerHandle`](struct@crate::manager::ProcessManagerHandle) documentation.
15//!
16//! ## API overview
17//! `Proc-heim` library is divided into two modules: `model` and `manager`.
18//!
19//! The first one defines types and traits used to describe commands, scripts and their settings, such as messaging and logging type, environment variables and working directory. The module contains a `Runnable` trait, which defines how to run a user-defined process. The library provides two implementation of this trait: `Cmd` and `Script`.
20//!
21//! The `manager` module provides an API for spawning and managing child processes. The whole implementation relies on `Actor model` architecture. To start using the library, a client code needs to spawn a `ProcessManager` task, responsible for:
22//! * creating new actors implementing some functionality (eg. reading messages from child process),
23//! * forwarding messages sent between client code and other actors.
24//!
25//! After spawning the `ProcessManager` task, a `ProcessManagerHandle` is being returned, which exposes an API for spawning and managing user-defined processes.
26//!
27//! ## Examples
28//!
29//! ### Spawning a new `ProcessManager` task
30//! ```no_run
31//! use proc_heim::manager::ProcessManager;
32//! use std::path::PathBuf;
33//!
34//! let working_directory = PathBuf::from("/some/temp/path");
35//! let handle = ProcessManager::spawn(working_directory).expect("Invalid working directory");
36//! // now use the handle to spawn new processes and interact with them
37//! ```
38//!
39//! ### Spawning a new process from command
40//! ```no_run
41//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
42//! use proc_heim::manager::ProcessManager;
43//! use std::path::PathBuf;
44//! use proc_heim::model::command::Cmd;
45//!
46//! let working_directory = PathBuf::from("/tmp/proc_heim");
47//! let handle = ProcessManager::spawn(working_directory)?;
48//! let cmd = Cmd::with_args("ls", ["-l", "/some/dir"]);
49//! let process_id = handle.spawn(cmd).await?;
50//! # Ok(()) }
51//! // now use the process_id to interact with a process ...
52//! ```
53//!
54//! ### Reading logs from a process
55//! ```no_run
56//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
57//! use proc_heim::{
58//! manager::{LogsQuery, ProcessManager},
59//! model::{
60//! command::{CmdOptions, LoggingType},
61//! script::{Script, ScriptingLanguage}
62//! },
63//! };
64//! use std::{path::PathBuf, time::Duration};
65//!
66//! let working_directory = PathBuf::from("/tmp/proc_heim");
67//! let handle = ProcessManager::spawn(working_directory)?;
68//! let script = Script::with_args_and_options(
69//! ScriptingLanguage::Bash,
70//! r#"
71//! echo 'Simple log example'
72//! echo "Hello $1"
73//! echo 'Error log' >&2
74//! "#,
75//! ["World"],
76//! CmdOptions::with_logging(LoggingType::StdoutAndStderr),
77//! );
78//!
79//! let process_id = handle.spawn(script).await?;
80//! // We are waiting for the process to exit in order to get all logs
81//! handle.wait(process_id, Duration::from_micros(10)).await??;
82//!
83//! let logs = handle
84//! .get_logs_stdout(process_id, LogsQuery::with_offset(1))
85//! .await?;
86//! assert_eq!(1, logs.len());
87//! assert_eq!("Hello World", logs[0]);
88//!
89//! let error_logs = handle
90//! .get_logs_stderr(process_id, LogsQuery::fetch_all())
91//! .await?;
92//! assert_eq!(1, error_logs.len());
93//! assert_eq!("Error log", error_logs[0]);
94//! # Ok(()) }
95//! ```
96//!
97//! ### Messaging with a process via named pipes
98//!
99//! ```no_run
100//! use futures::TryStreamExt;
101//! use proc_heim::{
102//! manager::{ProcessManager, TryMessageStreamExt},
103//! model::{
104//! command::CmdOptions,
105//! script::{Script, ScriptingLanguage},
106//! },
107//! };
108//! use std::path::PathBuf;
109//!
110//! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> {
111//! let working_directory = PathBuf::from("/tmp/proc_heim");
112//! let handle = ProcessManager::spawn(working_directory)?;
113//! let script = Script::with_options(
114//! ScriptingLanguage::Bash,
115//! r#"
116//! counter=0
117//! while read msg; do
118//! echo "$counter: $msg" > $OUTPUT_PIPE
119//! counter=$((counter + 1))
120//! done < $INPUT_PIPE
121//! "#,
122//! CmdOptions::with_named_pipe_messaging(), // we want to send messages bidirectionally
123//! );
124//!
125//! // We can use "spawn_with_handle" instead of "spawn" to get "ProcessHandle",
126//! // which mimics the "ProcessManagerHandle" API,
127//! // but without having to pass the process ID to each method call.
128//! let process_handle = handle.spawn_with_handle(script).await?;
129//!
130//! process_handle.send_message("First message").await?;
131//! // We can send a next message without causing a deadlock here.
132//! // This is possible because the response to the first message
133//! // will be read by a dedicated Tokio task,
134//! // spawned automatically by the Process Manager.
135//! process_handle.send_message("Second message").await?;
136//!
137//! let mut stream = process_handle
138//! .subscribe_message_stream()
139//! .await?
140//! .into_string_stream();
141//!
142//! let msg = stream.try_next().await?.unwrap();
143//! assert_eq!("0: First message", msg);
144//!
145//! let msg = stream.try_next().await?.unwrap();
146//! assert_eq!("1: Second message", msg);
147//!
148//! let result = process_handle.kill().await;
149//! assert!(result.is_ok());
150//! # Ok(()) }
151//! ```
152//!
153//! For more examples, see [integration tests](https://github.com/Heliwrenaid/proc-heim/tree/main/tests).
154mod process;
155mod working_dir;
156
157pub mod model {
158 //! Types and traits used for modeling commands, scripts and custom processes.
159 pub mod command {
160 //! Types representing commands and its settings.
161 //!
162 //! The two most important struct for modeling command is [`Cmd`] and [`CmdOptions`].
163 //! The first one defines command name and its arguments,
164 //! and the second is used to describe additional settings like command's input/outputs, working directory and environment variables.
165 //!
166 //! [`Cmd`] implements [`Runnable`](trait@crate::model::Runnable) trait, and therefore it can be spawned using [`ProcessManagerHandle`](struct@crate::manager::ProcessManagerHandle).
167 pub use crate::process::{
168 BufferCapacity, Cmd, CmdError, CmdOptions, CmdOptionsError, LoggingType, MessagingType,
169 };
170
171 /// Alternative API for creating [`Cmd`] and [`CmdOptions`] structures.
172 pub mod builder {
173 pub use crate::process::{CmdBuilder, CmdBuilderError, CmdOptionsBuilder};
174 }
175 }
176
177 pub mod script {
178 //! Types representing scripts.
179 //!
180 //! The most important types of this module are [`Script`] and [`ScriptingLanguage`] which enables of defining a scripts.
181 //! `Script` internally store script's content in a file and then executes [`Cmd`](struct@crate::model::command::Cmd).
182 //! [`ScriptingLanguage`] defines the language in which the script is implemented.
183 //! Currently, library supports 8 most popular scripting languages, but it is possible to support a custom ones via [`ScriptingLanguage::Other`].
184 //!
185 //! [`Script`] implements [`Runnable`](trait@crate::model::Runnable) trait, and therefore it can be spawned using [`ProcessManagerHandle`](struct@crate::manager::ProcessManagerHandle).
186 pub use crate::process::{
187 Script, ScriptRunConfig, ScriptingLanguage, SCRIPT_FILE_PATH_PLACEHOLDER,
188 };
189
190 /// Alternative API for creating [`Script`] structure.
191 pub mod builder {
192 pub use crate::process::{ScriptBuilder, ScriptBuilderError};
193 }
194 }
195
196 pub use crate::process::Runnable;
197}
198
199pub mod manager {
200 //! API used for spawning and managing multiple processes.
201 //!
202 //! The API is implemented by [`ProcessManager`], which spawns a `Tokio` task and exposes its functionalities through [`ProcessManagerHandle`].
203 //! `ProcessManagerHandle` communicates asynchronously with `ProcessManager` via message passing,
204 //! therefore it can be cheaply cloned and used by many threads concurrently without any lock-based synchronization.
205 //!
206 //! See [`ProcessManager`] and [`ProcessManagerHandle`] docs for more information.
207 pub use crate::process::{
208 GetLogsError, GetProcessInfoError, KillProcessError, LogsQuery, Message, MessageStreamExt,
209 ProcessHandle, ProcessId, ProcessInfo, ProcessManager, ProcessManagerHandle,
210 ReadMessageError, ReceiveDeserializedMessageError, ReceiveMessageError, ResultStreamExt,
211 ScopedProcessHandle, SpawnProcessError, TryMessageStreamExt, WriteMessageError,
212 INPUT_PIPE_ENV_NAME, OUTPUT_PIPE_ENV_NAME, PROCESS_DATA_DIR_ENV_NAME,
213 };
214
215 #[cfg(any(feature = "json", feature = "message-pack"))]
216 pub mod serde {
217 //! Types representing messages format and encoding.
218 pub use crate::process::{MessageFormat, SerdeError};
219
220 #[cfg(feature = "message-pack")]
221 pub use crate::process::Encoding;
222 }
223}