/* * Copyright 2026 Flix Authors * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */////// Effect for starting and managing processes outside the JVM, and its functions.///pubmod Sys.Process {use IoError.ErrorKinduse IoError.IoErroruse Sys.Processuse Time.TimeUnituse Sys.ProcessHandleuse Sys.ProcessHandle.ProcessHandleuse Sys.StdIn.StdInuse Sys.StdOut.StdOutuse Sys.StdErr.StdErrimport java.lang.IllegalArgumentExceptionimport java.lang.IllegalThreadStateExceptionimport java.lang.InterruptedExceptionimport java.lang.UnsupportedOperationExceptionimport java.io.IOExceptionimport java.lang.ProcessBuilderimport java.lang.{ProcessHandle => JProcessHandle}import java.lang.Runtimeimport java.io.{File => JFile}import java.util.concurrent.{TimeUnit => JTimeUnit}////// Effect for starting and managing processes outside the JVM.///pubeffProcess {////// Immediately executes the command `cmd` with the arguments `args`, by the path `cwd` and with the environment `env`.///defexecWithCwdAndEnv(cmd: String, args: List[String], cwd: Option[String], env: Map[String, String]): Result[IoError, ProcessHandle]////// Returns the exit value of the process `ph`.///defexitValue(ph: ProcessHandle): Result[IoError, Int32]////// Returns whether the process `ph` is alive.///defisAlive(ph: ProcessHandle): Result[IoError, Bool]////// Returns the PID of the process `ph`.///defpid(ph: ProcessHandle): Result[IoError, Int64]////// Returns the stdin stream of the process `ph`.///defstdin(ph: ProcessHandle): Result[IoError, StdIn]////// Returns the stdout stream of the process `ph`.///defstdout(ph: ProcessHandle): Result[IoError, StdOut]////// Returns the stderr stream of the process `ph`.///defstderr(ph: ProcessHandle): Result[IoError, StdErr]////// Stops the process `ph`.///defstop(ph: ProcessHandle): Result[IoError, Unit]////// Waits for the process `ph` to finish and returns its exit value.///defwaitFor(ph: ProcessHandle): Result[IoError, Int32]////// Waits at most `time` (in the given `tUnit`) for the process `ph` to finish./// Returns `true` if the process exited, `false` if the timeout elapsed.///defwaitForTimeout(ph: ProcessHandle, time: Int64, tUnit: TimeUnit): Result[IoError, Bool] }////// Executes the command `cmd` with the arguments `args`.///pubdefexec(cmd: String, args: List[String]): Result[IoError, ProcessHandle] \ Process =Process.execWithCwdAndEnv(cmd, args, None, Map.empty())////// Executes the command `cmd` with the arguments `args`, by the path `cwd`.///pubdefexecWithCwd(cmd: String, args: List[String], cwd: Option[String]): Result[IoError, ProcessHandle] \ Process =Process.execWithCwdAndEnv(cmd, args, cwd, Map.empty())////// Executes the command `cmd` with the arguments `args` and with the environmental `env`.///pubdefexecWithEnv(cmd: String, args: List[String], env: Map[String, String]): Result[IoError, ProcessHandle] \ Process =Process.execWithCwdAndEnv(cmd, args, None, env)////// Handles the `Process` effect of the given function `f`.////// In other words, re-interprets the `Process` effect using the `IO` effect.///pubdefhandle(f: a -> b \ ef): a -> b \ (ef - Process) + IO = x ->run {f(x) } withhandlerProcess {defexecWithCwdAndEnv(cmd, args, cwdPath, env, k) = regionrc {letres = try {letarr = List.toArray(rc, cmd :: args);letpb = new ProcessBuilder(arr);foreach (e <- Map.toList(env)) {pb.environment().put(fst(e), snd(e));() };matchcwdPath {case Some(cwd) => pb.directory(new JFile(cwd)); ()case None => () };letprocess = pb.start(); Ok(ProcessHandle(process)) } catch {caseex: IllegalArgumentException => Err(IoError(ErrorKind.InvalidInput, ex.getMessage()))caseex: IOException => Err(IoError(ErrorKind.Other, ex.getMessage())) };k(res) }defexitValue(ph, k) =try {let ProcessHandle(p) = ph;k(Ok(p.exitValue())) } catch {caseex: IllegalThreadStateException => k(Err(IoError(ErrorKind.Other, ex.getMessage())))caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defisAlive(ph, k) =try {let ProcessHandle(p) = ph;k(Ok(p.isAlive())) } catch {caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defpid(ph, k) =try {let ProcessHandle(p) = ph;letpid = p.pid();k(Ok(pid)) } catch {caseex: UnsupportedOperationException => k(Err(IoError(ErrorKind.Unsupported, ex.getMessage())))caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defstdin(ph, k) =try {let ProcessHandle(p) = ph;k(Ok(StdIn(p.getOutputStream()))) } catch {caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defstdout(ph, k) =try {let ProcessHandle(p) = ph;k(Ok(StdOut(p.getInputStream()))) } catch {caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defstderr(ph, k) =try {let ProcessHandle(p) = ph;k(Ok(StdErr(p.getErrorStream()))) } catch {caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defstop(ph, k) =try {let ProcessHandle(p) = ph;k(Ok(p.destroy())) } catch {caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defwaitFor(ph, k) =try {let ProcessHandle(p) = ph;letexitVal = p.waitFor();k(Ok(exitVal)) } catch {caseex: InterruptedException => k(Err(IoError(ErrorKind.Interrupted, ex.getMessage())))caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) }defwaitForTimeout(ph, time, unit, k) =try {lettimeMS = time*Int32.toInt64(TimeUnit.toMilliseconds(unit));let ProcessHandle(p) = ph;letexitVal = p.waitFor(timeMS, JTimeUnit.MILLISECONDS);k(Ok(exitVal)) } catch {caseex: InterruptedException => k(Err(IoError(ErrorKind.Interrupted, ex.getMessage())))caseex: IOException => k(Err(IoError(ErrorKind.Other, ex.getMessage()))) } }////// Runs the `Process` effect of the given function `f`.////// In other words, re-interprets the `Process` effect using the `IO` effect.///@DefaultHandlerpubdefrunWithIO(f: Unit -> a \ ef): a \ (ef - Process) + IO = handle(f)()}