AsynchronousInteractiveProcess

Struct AsynchronousInteractiveProcess 

Source
pub struct AsynchronousInteractiveProcess {
    pub pid: Option<u32>,
    pub filename: String,
    pub arguments: Vec<String>,
    pub working_directory: PathBuf,
    /* private fields */
}
Expand description

AsynchronousInteractiveProcess is a structure that represents a non-blocking, interactive process. It provides metadata and mechanisms to interact with a process asynchronously using sender and receiver channels.

§Fields

  • pid - An optional process ID (pid) of the interactive process. If the process is not yet started, this field will remain None.

  • filename - The name of the executable file that represents the interactive process. This is required to identify and initiate the process.

  • arguments - A vector containing the arguments to pass to the executable file when launching the process.

  • working_directory - The directory where the interactive process will be executed. This is represented as a PathBuf.

  • sender - An optional Sender channel used to send messages or data to the interactive process. This field is skipped during serialization and deserialization because it holds runtime-related data.

  • receiver - An optional Receiver channel used to receive messages or data from the interactive process. This field is also skipped during serialization and deserialization for the same reasons as sender.

  • input_queue - A deque (double-ended queue) that maintains an in-memory buffer of strings representing input for the interactive process. This field is skipped during serialization as it only contains transient runtime-related data.

§Trait Implementations

  • Debug: Allows instances of this struct to be formatted and logged for debugging purposes.
  • Serialize: Makes the struct serializable, excluding fields marked with #[serde(skip)].
  • Deserialize: Allows deserialization to create instances of this struct from serialized data.
  • Default: Provides a default implementation where optional fields are set to None, collections are empty, and filename is an empty string.

§Example

use std::path::PathBuf;
use std::collections::VecDeque;
use serde::{Serialize, Deserialize};
use crossbeam_channel::{Sender, Receiver};

let process = AsynchronousInteractiveProcess {
    pid: None,
    filename: "my_program".to_string(),
    arguments: vec!["--help".to_string()],
    working_directory: PathBuf::from("/path/to/dir"),
    sender: None,
    receiver: None,
    input_queue: VecDeque::new(),
};

println!("{:?}", process);

This structure is ideal for scenarios where processes need to be controlled asynchronously with multiple input or output channels.

Fields§

§pid: Option<u32>§filename: String§arguments: Vec<String>§working_directory: PathBuf

Implementations§

Source§

impl AsynchronousInteractiveProcess

Source

pub fn new(filename: impl Into<String>) -> Self

Creates a new instance of the struct with default values.

§Arguments
  • filename - A value that can be converted into a String. This typically represents the name or path of the file associated with the instance.
§Returns

Returns a new instance of the struct populated with default fields:

  • pid: None (indicating no process ID is associated yet).
  • filename: The provided filename converted into a String.
  • arguments: An empty vector, representing no initial arguments.
  • working_directory: Set to the current directory ("./").
  • sender: None, indicating no sender is associated initially.
  • receiver: None, indicating no receiver is associated initially.
  • input_queue: An empty VecDeque, representing no items in the input queue.
§Example
let instance = MyStruct::new("example.txt");
assert_eq!(instance.filename, "example.txt");
assert!(instance.pid.is_none());
assert!(instance.arguments.is_empty());
assert_eq!(instance.working_directory, PathBuf::from("./"));
assert!(instance.sender.is_none());
assert!(instance.receiver.is_none());
assert!(instance.input_queue.is_empty());
Source

pub fn with_arguments(self, args: Vec<impl Into<String>>) -> Self

Sets the arguments for the current instance by converting a vector of items implementing Into<String> into a Vec<String>.

§Parameters
  • args: A Vec containing items that implement the Into<String> trait. These items will be converted into String and used to set the arguments field of the instance.
§Returns
  • Self: The current instance (self) after updating its arguments field.
§Example
let instance = MyStruct::new()
    .with_arguments(vec!["arg1", "arg2", String::from("arg3")]);

This method allows chaining, as it returns the updated instance after setting the arguments field.

Source

pub fn with_argument(self, arg: impl Into<String>) -> Self

Adds an argument to the arguments vector and returns the modified instance.

§Parameters
  • arg - A value that implements the Into<String> trait, which will be converted into a String and added to the arguments vector.
§Returns

Returns the modified instance of the implementing struct (Self) with the new argument added.

§Example
let instance = SomeStruct::new().with_argument("example");

This adds the string "example" to the arguments vector of SomeStruct.

Source

pub fn with_working_directory(self, dir: impl Into<PathBuf>) -> Self

Sets the working directory for the instance and returns the modified instance.

§Arguments
  • dir - A value that can be converted into a PathBuf, representing the desired working directory.
§Returns

The instance of the struct with the updated working directory.

§Example
let instance = MyStruct::new()
    .with_working_directory("/path/to/directory");
Source

pub fn process_exit_callback<F>(self, callback: F) -> Self
where F: Fn(i32) + Send + Sync + 'static,

Source

pub async fn start(&mut self) -> Result<u32>

Starts a new process based on the configuration stored in the struct, manages its I/O streams asynchronously, and tracks its lifecycle in a shared process pool.

§Returns
  • Ok<u32>: Returns the process ID (PID) if the process starts successfully.
  • Err(std::io::Error): Returns an error if any part of the process start operation fails.
§Process Configuration
  • The process is launched using the executable specified in self.filename.
  • Command-line arguments are passed via self.arguments.
  • The process will run in the directory specified in self.working_directory.
§Standard I/O Management
  • The process’s stdin is assigned a tokio::sync::mpsc::channel for communication.
  • stdout and stderr are read asynchronously. Each line from these streams is sent to an mpsc::channel, where stdout/stderr data can be consumed.
§Shared Process Pool
  • The process metadata, including its PID and I/O channels, is stored in a global, thread-safe PROCESS_POOL.
  • The process pool is managed using a tokio::sync::Mutex and stores processes in a HashMap with their PID as the key.
§Asynchronous I/O
  • A background task is spawned to write data from an internal input queue to the process’s stdin.
  • Another background task continuously reads and forwards stdout and stderr messages from the process.
  • If the process exits or encounters an error, its PID and metadata are removed from the process pool.
§Example Use Case
let mut my_process = MyProcess {
    filename: "my_program".to_string(),
    arguments: vec!["--arg1", "value1".to_string()],
    working_directory: "/path/to/dir".to_string(),
    pid: None,
    sender: None,
    receiver: None,
    input_queue: VecDeque::new(),
};

let pid = my_process.start().await.unwrap();
println!("Started process with PID: {}", pid);
§Notes
  • The process’s stdin, stdout, and stderr are piped to capture and manage communication asynchronously.
  • Each spawned background task is independently responsible for managing a specific stream or part of the process lifecycle.
  • Errors during I/O operations or spawning the process are logged using the log crate macros (e.g., error!, debug!).
§Potential Errors
  • Failure to spawn the process (e.g., if self.filename is invalid or inaccessible).
  • Errors during communication with the process’s I/O streams (e.g., writing to a closed stdin).
  • Mutex locking failure on the shared process pool due to an internal inconsistency.
Source

pub async fn get_process_by_pid(pid: u32) -> Option<ProcessHandle>

Asynchronously retrieves a handle to a process for a given process ID (PID).

This function checks if a process with the specified PID exists in a shared process pool. If the process is found, it returns an Option containing a ProcessHandle for the process. Otherwise, it returns None.

§Arguments
  • pid - A 32-bit unsigned integer representing the process ID of the desired process.
§Returns
  • Some(ProcessHandle) - If a process with the given PID is found in the process pool.
  • None - If the process does not exist in the process pool or if the process pool is uninitialized.
§Example
if let Some(handle) = get_process_by_pid(12345).await {
    println!("Process found with PID: {}", handle.pid);
} else {
    println!("Process not found.");
}
§Errors

This function will return None if the global process pool (PROCESS_POOL) has not been initialized or is unavailable.

§Note

This is an asynchronous function and must be awaited to complete its operation.

Source

pub async fn is_process_running(&self) -> bool

Asynchronously checks if a process identified by its pid is currently running.

§Returns
  • true if the process with the stored pid is found in the PROCESS_POOL.
  • false if the stored pid is None, the PROCESS_POOL is not initialized, or the pid does not exist in the PROCESS_POOL.

The function first checks if the pid instance variable is set. If so, it attempts to access the global PROCESS_POOL. If PROCESS_POOL is initialized, it acquires a lock and checks if the pid exists in the pool.

§Examples
let result = my_instance.is_process_running().await;
if result {
    println!("The process is running.");
} else {
    println!("The process is not running.");
}
§Async Behavior

This function acquires an asynchronous lock on the PROCESS_POOL to safely access its contents and will yield if the lock is currently held elsewhere.

§Panics

This function may panic if the async lock on the PROCESS_POOL fails unexpectedly.

§Dependencies
  • PROCESS_POOL should be a globally accessible and lazily initialized structure (e.g., using once_cell or similar patterns) that maintains a mapping of currently active processes.

Trait Implementations§

Source§

impl Debug for AsynchronousInteractiveProcess

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for AsynchronousInteractiveProcess

Source§

fn default() -> AsynchronousInteractiveProcess

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for AsynchronousInteractiveProcess

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for AsynchronousInteractiveProcess

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

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> 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, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,