tcrm-task 0.4.2

Task execution unit for TCRM project
Documentation

TCRM Task

Async task execution unit for the TCRM project

Tested on Windows 10. Not tested on Unix.

Features

  • Asynchronous Execution: Built on Tokio for async task execution
  • Task Timeout: Configurable execution timeout
  • Event System: Real-time monitoring of task lifecycle and output
  • Optional Tracing/Logging: Enable structured logging with the tracing Cargo feature

Installation

Add this to your Cargo.toml:

[dependencies]

tcrm-task = { version = "0.3.2" }

Quick Start

Basic Task Execution

use tcrm_task::tasks::{
    config::TaskConfig,
    tokio::spawn::spawner::TaskSpawner,
    event::{TaskEvent, TaskEventEnvelope},
};
use tokio::sync::mpsc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let config = if cfg!(windows) {
        TaskConfig::new("powershell")
            .args(["-Command", "echo Hello from Windows!"])
            .timeout_ms(5000)
    } else {
        TaskConfig::new("bash")
            .args(["-c", "echo Hello from Unix!"])
            .timeout_ms(5000)
    };

    let mut spawner = TaskSpawner::new("hello_task".to_string(), config);

    // Create an event channel to receive task events
    let (event_tx, mut event_rx) = mpsc::channel::<TaskEventEnvelope>(100);

    // Start the task
    let process_id = spawner.start_direct(event_tx).await?;
    println!("Started process with ID: {}", process_id);

    // Listen for events
    while let Some(envelope) = event_rx.recv().await {
        match envelope.event {
            TaskEvent::Started { process_id, .. } => {
                println!("Task '{}' started with PID {}", envelope.id, process_id);
            }
            TaskEvent::Output { line, src, .. } => {
                println!("Task '{}' output ({:?}): {}", envelope.id, src, line);
            }
            TaskEvent::Stopped { exit_code, reason, .. } => {
                println!("Task '{}' stopped with exit code {:?}, reason: {:?}", 
                    envelope.id, exit_code, reason);
                break;
            }
            TaskEvent::Error { error } => {
                eprintln!("Task '{}' error: {}", envelope.id, error);
            }
            _ => {}
        }
    }
    Ok(())
}

More Configuration

use tcrm_task::tasks::config::TaskConfig;
use std::collections::HashMap;

let config = TaskConfig::new("cargo")
    .args(["build", "--release"])
    .working_dir("/path/to/project")
    .env([
        ("RUST_LOG", "debug"),
        ("CARGO_TARGET_DIR", "target")
    ])
    .timeout_ms(30000)  // 30 seconds
    .enable_stdin(true);

// Validate configuration before use
config.validate()?;

Task with Stdin Input

use tokio::sync::mpsc;

// Create stdin channel
let (stdin_tx, stdin_rx) = mpsc::channel::<String>(10);
let config = if cfg!(windows) {
    TaskConfig::new("powershell")
        .args(["-Command", "cat"])
        .enable_stdin(true)
} else {
    TaskConfig::new("cat")
        .enable_stdin(true)
};
let mut spawner = TaskSpawner::new("cat_task".to_string(), config)
    .set_stdin(stdin_rx);
stdin_tx.send("Hello from stdin!".to_string()).await?;

Task States

Tasks progress through the following states:

  • Pending: Task is created but not yet started
  • Initiating: Task is being prepared for execution
  • Running: Task is actively executing
  • Ready: Task is running and ready (for long-running processes)
  • Finished: Task has completed execution

Event System

The library provides real-time events for task monitoring:

use tcrm_task::tasks::event::{TaskEvent, TaskStopReason};

// Events are wrapped in TaskEventEnvelope
while let Some(envelope) = event_rx.recv().await {
    match envelope.event {
        TaskEvent::Started { process_id, .. } => {
            // Task has started
            println!("Task '{}' started with PID {}", envelope.id, process_id);
        }
        TaskEvent::Output { line, src, .. } => {
            // New output line from stdout or stderr
            println!("Task '{}' output: {}", envelope.id, line);
        }
        TaskEvent::Ready => {
            // Task is ready
            println!("Task '{}' is ready", envelope.id);
        }
        TaskEvent::Stopped { exit_code, reason, .. } => {
            // Task has stopped
            match reason {
                TaskStopReason::Finished => println!("Task '{}' completed normally", envelope.id),
                TaskStopReason::Terminated(reason) => println!("Task '{}' terminated: {:?}", envelope.id, reason),
                TaskStopReason::Error(err) => println!("Task '{}' failed: {}", envelope.id, err),
            }
        }
        TaskEvent::Error { error } => {
            // Task encountered an error
            eprintln!("Task '{}' error: {}", envelope.id, error);
        }
        _ => {}
    }
}

Features

Default Features

  • tokio: Enables async functionality (enabled by default)

Optional Features

  • flatbuffers: Enables FlatBuffers serialization support
  • tracing: Enables structured logging/tracing macros

Examples

See the examples/ directory for:

  • Basic process execution
  • Interactive process with stdin
  • Configuration validation
  • Tracing/logging output

Testing

Run the test suite:

# Run all tests

cargo test


# Run tests with logging/tracing

RUST_LOG=debug cargo test --features tracing


# Run specific test module

cargo test tasks::tests

License

This project is licensed under either the MIT or Apache-2.0 License, at your option.

See LICENSE file for details.