// process_pool: Moonbit Process Pool Library
//
// A task runner for parallel process execution.
//
// Design:
// - Semaphore limits concurrent execution count
// - TaskGroup provides structured concurrency
// - @process.run executes child processes
//
// Example:
// let pool = @process_pool.ProcessPool::new(max_workers=4)
// let results = pool.run_all([
// @process_pool.job("echo", ["hello"]),
// @process_pool.job("echo", ["world"]),
// ])
//
// Supported platforms:
// - native: Full functionality
// - js: Full functionality in Node.js environment
// - wasm/wasm-gc: Not implemented (abort)
///|
/// Job definition
pub struct Job {
cmd : String
args : Array[String]
cwd : String?
timeout : Int? // Timeout in milliseconds
}
///|
/// Job execution result
pub struct JobResult {
job : Job
exit_code : Int
stdout : String
stderr : String
timed_out : Bool // Whether the job timed out
}
///|
/// Job execution error
pub suberror JobError {
ProcessFailed(
job~ : Job,
exit_code~ : Int,
stdout~ : String,
stderr~ : String
)
ProcessTimedOut(
job~ : Job,
timeout~ : Int,
stdout~ : String,
stderr~ : String
)
}
///|
/// Callback invoked on job completion
pub(all) struct OnJobComplete((JobResult) -> Unit)
///|
/// Helper function to create a job
///
/// Parameters:
/// - `cmd`: Command to execute
/// - `args`: Command arguments
/// - `cwd`: Working directory (optional)
/// - `timeout`: Timeout in milliseconds (optional)
pub fn job(
cmd : String,
args : Array[String],
cwd? : String,
timeout? : Int,
) -> Job {
Job::{ cmd, args, cwd, timeout }
}
///|
/// Run multiple jobs in parallel and raise an exception on error
///
/// If any job fails (exit_code != 0) or times out,
/// raises JobError.
pub async fn ProcessPool::run_all_checked(
self : ProcessPool,
jobs : Array[Job],
) -> Array[JobResult] {
let results = self.run_all(jobs)
for result in results {
if result.timed_out {
raise JobError::ProcessTimedOut(
job=result.job,
timeout=result.job.timeout.unwrap_or(0),
stdout=result.stdout,
stderr=result.stderr,
)
}
if result.exit_code != 0 {
raise JobError::ProcessFailed(
job=result.job,
exit_code=result.exit_code,
stdout=result.stdout,
stderr=result.stderr,
)
}
}
results
}
///|
/// Transform input data into jobs and run them in parallel
///
/// Useful for SSG and similar use cases where jobs are generated
/// from file lists and executed in parallel.
pub async fn[T] ProcessPool::map(
self : ProcessPool,
items : Array[T],
to_job : (T) -> Job,
) -> Array[JobResult] {
let jobs = items.map(to_job)
self.run_all(jobs)
}