TaskExecutor

Struct TaskExecutor 

Source
pub struct TaskExecutor { /* private fields */ }
Expand description

Task executor for managing process lifecycle

TaskExecutor is the main entry point for executing system processes with real-time event monitoring, timeout management, and cross-platform process control. It coordinates process spawning, I/O handling, and termination through an event-driven architecture built on tokio.

§Examples

§Basic Process Execution

use tcrm_task::tasks::{config::TaskConfig, tokio::executor::TaskExecutor};
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    #[cfg(windows)]
    let config = TaskConfig::new("cmd").args(["/C", "echo", "Hello, World!"]);
    #[cfg(unix)]
    let config = TaskConfig::new("echo").args(["Hello, World!"]);
     
    config.validate()?;
     
    let (tx, mut rx) = mpsc::channel(100);
    let mut executor = TaskExecutor::new(config, tx);
     
    executor.coordinate_start().await?;
     
    while let Some(event) = rx.recv().await {
        match event {
            tcrm_task::tasks::event::TaskEvent::Output { line, .. } => {
                println!("Output: {}", line);
            }
            tcrm_task::tasks::event::TaskEvent::Stopped { .. } => break,
            _ => {}
        }
    }
     
    Ok(())
}

§Process with Timeout and Termination

use tcrm_task::tasks::{
    config::TaskConfig,
    tokio::executor::TaskExecutor,
    control::TaskControl,
    event::TaskTerminateReason
};
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    #[cfg(windows)]
    let config = TaskConfig::new("cmd")
        .args(["/C", "timeout", "/t", "10"])
        .timeout_ms(5000);
    #[cfg(unix)]
    let config = TaskConfig::new("sleep")
        .args(["10"])
        .timeout_ms(5000);
     
    let (tx, mut rx) = mpsc::channel(100);
    let mut executor = TaskExecutor::new(config, tx);
     
    executor.coordinate_start().await?;
     
    // Terminate after 2 seconds
    tokio::spawn(async move {
        tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
        let _ = executor.terminate_task(TaskTerminateReason::UserRequested);
    });
     
    while let Some(event) = rx.recv().await {
        if matches!(event, tcrm_task::tasks::event::TaskEvent::Stopped { .. }) {
            break;
        }
    }
     
    Ok(())
}

Implementations§

Source§

impl TaskExecutor

Source

pub fn new(config: TaskConfig, event_tx: Sender<TaskEvent>) -> Self

Create a new task executor with the given configuration

§Arguments
  • config - Validated task configuration containing command, arguments, and options
  • event_tx - Channel for sending task events
§Examples
use tcrm_task::tasks::{config::TaskConfig, tokio::executor::TaskExecutor};
use tokio::sync::mpsc;

#[cfg(windows)]
let config = TaskConfig::new("cmd").args(["/C", "dir"]);
#[cfg(unix)]
let config = TaskConfig::new("ls").args(["-la"]);

let (tx, _rx) = mpsc::channel(100);
let executor = TaskExecutor::new(config, tx);
Source

pub fn set_drop_event_tx_on_finished(&self, drop: bool)

Configures whether to drop (close) the event channel when the task finishes.

By default, the event channel (event_tx) is dropped when the task finishes, signaling to receivers that no more events will be sent. This method allows you to override that behavior, which is useful if you want to keep the event channel open for multiple tasks or for manual control.

§Arguments
  • drop - If true, the event channel will be dropped when the task finishes (default behavior). If false, the event channel will remain open after task completion.
§Example
executor.set_drop_event_tx_on_finished(false); // Keep event channel open after task finishes
Source§

impl TaskExecutor

Source

pub async fn send_stdin( &mut self, input: impl Into<String>, ) -> Result<(), TaskError>

Sends input to the process’s stdin.

Writes the provided input to the process’s stdin stream. The input will be automatically terminated with a newline if it doesn’t already end with one.

§Arguments
  • input - The input string to send to stdin
§Returns
  • Ok(()) - If the input was sent successfully
  • Err(TaskError) - If sending fails or the task is not running
§Errors

Returns TaskError::Control if the task is not in a running state, or TaskError::IO if writing to stdin fails

Source§

impl TaskExecutor

Source

pub async fn coordinate_start(&mut self) -> Result<(), TaskError>

Start execution in a coordinated async event loop.

This is the main execution method that spawns the process, sets up event monitoring, and manages the complete process lifecycle. It handles stdout/stderr streaming, timeout management, termination signals, and process cleanup in a coordinated async event loop using `tokio::select!``.

§Arguments
  • event_tx - Channel sender for emitting [TaskEvent]s during process execution
§Returns
  • Ok(()) - Process coordination started successfully
  • Err(TaskError) - Configuration validation or process spawning failed
§Errors

Returns TaskError for:

§Events Emitted

During execution, the following events are sent via event_tx:

  • [TaskEvent::Started] - Process spawned successfully
  • [TaskEvent::Output] - Lines from stdout/stderr
  • [TaskEvent::Ready] - Ready indicator detected (if configured)
  • [TaskEvent::Stopped] - Process completed with exit code
  • [TaskEvent::Error] - Errors during execution
§Examples
§Basic Usage
use tcrm_task::tasks::{config::TaskConfig, tokio::executor::TaskExecutor};
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    #[cfg(windows)]
    let config = TaskConfig::new("cmd").args(["/C", "echo", "test"]);
    #[cfg(unix)]
    let config = TaskConfig::new("echo").args(["test"]);
     
    config.validate()?;
     
    let (tx, mut rx) = mpsc::channel(100);
    let mut executor = TaskExecutor::new(config, tx);
     
    // Start coordination - returns immediately, process runs in background
    executor.coordinate_start().await?;
     
    // Process events until completion
    while let Some(event) = rx.recv().await {
        println!("Event: {:?}", event);
        if matches!(event, tcrm_task::tasks::event::TaskEvent::Stopped { .. }) {
            break;
        }
    }
     
    Ok(())
}
§With Ready Indicator
use tcrm_task::tasks::{
    config::{TaskConfig, StreamSource},
    tokio::executor::TaskExecutor,
    event::TaskEvent
};
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    #[cfg(windows)]
    let config = TaskConfig::new("cmd")
        .args(["/C", "echo", "Server ready"])
        .ready_indicator("Server ready")
        .ready_indicator_source(StreamSource::Stdout);
     
    #[cfg(unix)]
    let config = TaskConfig::new("echo")
        .args(["Server ready"])
        .ready_indicator("Server ready")
        .ready_indicator_source(StreamSource::Stdout);
     
    let (tx, mut rx) = mpsc::channel(100);
    let mut executor = TaskExecutor::new(config, tx);
     
    executor.coordinate_start().await?;
     
    while let Some(event) = rx.recv().await {
        match event {
            TaskEvent::Ready => {
                println!("Process is ready!");
                // Can now interact with the running process
            }
            TaskEvent::Stopped { .. } => break,
            _ => {}
        }
    }
     
    Ok(())
}

Trait Implementations§

Source§

impl Debug for TaskExecutor

Source§

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

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

impl ProcessControl for TaskExecutor

Source§

async fn perform_process_action( &mut self, action: ProcessControlAction, ) -> Result<(), TaskError>

Performs a control action on the process or process group. Read more
Source§

fn stop_process(&mut self) -> impl Future<Output = Result<(), TaskError>>

Requests the process to stop execution. Read more
Source§

fn pause_process(&mut self) -> impl Future<Output = Result<(), TaskError>>

Requests the process to pause execution. Read more
Source§

fn resume_process(&mut self) -> impl Future<Output = Result<(), TaskError>>

Requests the process to resume execution if it is paused. Read more
Source§

impl TaskControl for TaskExecutor

Source§

fn terminate_task( &mut self, reason: TaskTerminateReason, ) -> Result<(), TaskError>

Terminates the task with the specified reason. Read more
Source§

impl TaskStatusInfo for TaskExecutor

Source§

fn get_task_state(&self) -> TaskState

Gets the current state of the task. Read more
Source§

fn get_process_state(&self) -> ProcessState

Gets the current state of the process. Read more
Source§

fn get_process_id(&self) -> Option<u32>

Gets the process ID of the running task. Read more
Source§

fn get_create_at(&self) -> SystemTime

Gets the creation timestamp of the task. Read more
Source§

fn get_running_at(&self) -> Option<SystemTime>

Gets the timestamp when the task started running. Read more
Source§

fn get_finished_at(&self) -> Option<SystemTime>

Gets the timestamp when the task finished. Read more
Source§

fn get_exit_code(&self) -> Option<i32>

Gets the exit code of the finished task. Read more
Source§

fn get_last_signal_code(&self) -> Option<Signal>

Gets the last received signal (if any) from the process. Read more
Source§

fn get_information(&self) -> TaskInformation

Gets all information about the task. 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.