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