Async task execution unit for the TCRM project
Tested on Windows 10. Not tested on Unix.
- 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
Add this to your Cargo.toml
:
[dependencies]
tcrm-task = { version = "0.3.2" }
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(())
}
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()?;
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?;
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
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);
}
_ => {}
}
}
tokio
: Enables async functionality (enabled by default)
flatbuffers
: Enables FlatBuffers serialization supporttracing
: Enables structured logging/tracing macros
See the examples/
directory for:
- Basic process execution
- Interactive process with stdin
- Configuration validation
- Tracing/logging output
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
This project is licensed under either the MIT or Apache-2.0 License, at your option.
See LICENSE file for details.