silk/child_process
Portable blocking child execution from structured byte arguments to fully captured output.
When to use
Build a ProcessRequest when one executable should run directly. This API is not a shell:
spaces, quotes, and metacharacters in an argument remain data and are never parsed as command
syntax.
Details
Requests preserve NUL-free argument and environment-entry bytes in insertion order. The
environment begins empty, and the working directory is inherited unless requestWithin
selects one. Child standard input is closed. ChildProcess.execute blocks until termination and
owns complete stdout and stderr captures in ProcessOutcome. A nonzero exit is outcome data.
ProcessError is reserved for failing to spawn, wait, or capture; it carries a stable
portable reason and may retain a provider code. Execution also reports OutOfMemoryError when
captured output cannot be owned.
Gotchas
An argument, environment name, or environment value must not contain NUL. The request uses NUL as its entry separator, and a native provider cannot preserve an embedded NUL as data.
Examples
Handle a nonzero child exit as outcome data
import silk.bytes as Bytes
import silk.child_process as Process
import silk.allocator { Allocator }
import silk.effect { Effect }
import silk.filesystem as Path
import silk.option as Option
struct Completed {}
effect fn execute(self: &mut Completed, request: &Process.ProcessRequest) -> Process.ProcessOutcome
! Process.ProcessError | Allocator.OutOfMemoryError
? &mut Allocator {
return Process.exited(7, Bytes.make(), Bytes.make())
}
impl Process.ChildProcess for Completed {
execute: Completed.execute
}
effect fn program() -> i32
! Path.FileError | Allocator.OutOfMemoryError | Process.ProcessError {
let mut allocator = Allocator.systemAllocatorProvider()
let mut provider = Completed {}
let path = run Path.make("/tool")
|> Effect.provideMut<Allocator>(&mut allocator)
let request = run Process.request(&path)
|> Effect.provideMut<Allocator>(&mut allocator)
let outcome = run Process.submit(&request)
|> Effect.provideMut<Process.ChildProcess>(&mut provider)
|> Effect.provideMut<Allocator>(&mut allocator)
return match move Process.exitCode(&outcome) {
Option.Some<i32> {value} => 35 + value
Option.None {} => 1
}
}
effect fn recover(error: Path.FileError | Allocator.OutOfMemoryError | Process.ProcessError) -> i32 {
return 0
}
pub fn main() -> i32 {
return run Effect.catchAll(program(), recover)
}Import as ChildProcess with import silk.child_process.
Public declarations: 41.
ProcessOperation
pub struct ProcessOperationA provider-reported execution stage for one ProcessError.
Field code
pub code: i32The stable numeric code for the provider-reported execution stage.
ProcessReason
pub struct ProcessReasonA portable recovery category for one ProcessError.
Field code
pub code: i32The stable numeric code for the portable recovery category.
ProcessError
pub struct ProcessErrorA typed failure to start, wait for, or capture one child process.
Details
operation identifies the provider-reported stage. reason gives a portable recovery category.
Use providerCode when diagnostics also need a provider-defined numeric code.
A child that exits with a nonzero code produces ProcessOutcome data instead of this error.
Field operation
pub operation: ProcessOperationThe execution stage reported by the provider.
Field reason
pub reason: ProcessReasonThe portable category that callers can use for recovery.
spawnOperation
pub fn spawnOperation() -> ProcessOperationReturns the stage for preparing and starting the child process.
waitOperation
pub fn waitOperation() -> ProcessOperationReturns the stage for waiting until the child process terminates.
captureOperation
pub fn captureOperation() -> ProcessOperationReturns the stage for copying the completed output and error streams.
operationCode
pub fn operationCode(operation: ProcessOperation) -> i32Returns the stable numeric code for an execution stage.
notFound
pub fn notFound() -> ProcessReasonReturns the reason used when no executable exists at the requested path.
permissionDenied
pub fn permissionDenied() -> ProcessReasonReturns the reason used when the provider denies access to the requested executable.
invalidRequest
pub fn invalidRequest() -> ProcessReasonReturns the reason used when the provider cannot present the request to its process boundary.
noSpace
pub fn noSpace() -> ProcessReasonReturns the reason used when process setup or captured output exhausts provider storage.
unsupported
pub fn unsupported() -> ProcessReasonReturns the reason used when the provider does not support the requested process operation.
other
pub fn other() -> ProcessReasonReturns the reason used when no other portable recovery category applies.
reasonCode
pub fn reasonCode(reason: ProcessReason) -> i32Returns the stable numeric code for a portable recovery category.
failure
pub fn failure(operation: ProcessOperation, reason: ProcessReason) -> ProcessErrorCreates a process failure without a provider-defined numeric code.
failureWithCode
pub fn failureWithCode(operation: ProcessOperation, reason: ProcessReason, code: i32) -> ProcessErrorCreates a process failure with a provider-defined numeric code for diagnostics.
providerCode
pub fn providerCode(error: &silk/child_process.ProcessError) -> Option<i32>Returns the provider-defined numeric code, or None when the failure has no such code.
ProcessRequest
pub struct ProcessRequestOne owned child-process request with ordered arguments and an explicit environment.
Details
Arguments and environment entries are exact platform bytes rather than checked text, so a value received from the platform can be handed to a child unchanged. Entries are retained in the order they were added, and the environment starts empty: a child sees no variable that this request did not name.
Gotchas
Argument, environment-name, and environment-value bytes must not contain NUL. NUL separates entries in the provider request format.
request
pub effect fn request(program: &silk/filesystem.Path) -> ProcessRequest ! OutOfMemoryError ? &mut AllocatorCreates a request for program with no arguments, an empty environment, and the caller's
own working directory.
requestWithin
pub effect fn requestWithin(program: &silk/filesystem.Path, directory: &silk/filesystem.Path) -> ProcessRequest ! OutOfMemoryError ? &mut AllocatorCreates a request that runs program in directory instead of the caller's
working directory.
addArgument
pub effect fn addArgument(self: &mut silk/child_process.ProcessRequest, value: &[u8]) -> () ! OutOfMemoryError ? &mut AllocatorAppends one NUL-free byte argument after all arguments already in the request.
Details
The request copies value and preserves argument order.
Gotchas
value must not contain NUL. NUL is the entry separator used by process providers.
If allocation fails, do not reuse self; it can contain an incomplete argument entry.
setVariable
pub effect fn setVariable(self: &mut silk/child_process.ProcessRequest, name: &[u8], value: &[u8]) -> () ! OutOfMemoryError ? &mut AllocatorAppends one NUL-free environment entry as name, =, and value bytes.
Details
The request starts with an empty environment and preserves insertion order. This function does not read or merge the caller's environment.
Gotchas
name and value must not contain NUL. The request builder does not validate environment-name
grammar beyond this provider-format requirement.
If allocation fails, do not reuse self; it can contain an incomplete environment entry.
program
pub fn program(self: &silk/child_process.ProcessRequest) -> &[u8]Borrows the executable path bytes for the lifetime of the request borrow.
arguments
pub fn arguments(self: &silk/child_process.ProcessRequest) -> &[u8]Borrows all ordered arguments as one block of NUL-terminated entries.
argumentCount
pub fn argumentCount(self: &silk/child_process.ProcessRequest) -> usizeReturns the number of calls to addArgument that completed successfully.
environment
pub fn environment(self: &silk/child_process.ProcessRequest) -> &[u8]Borrows the explicit environment as one block of NUL-terminated name=value entries.
environmentCount
pub fn environmentCount(self: &silk/child_process.ProcessRequest) -> usizeReturns the number of calls to setVariable that completed successfully.
workingDirectory
pub fn workingDirectory(self: &silk/child_process.ProcessRequest) -> &[u8]Borrows the selected working-directory bytes, or an empty view when the child inherits one.
hasWorkingDirectory
pub fn hasWorkingDirectory(self: &silk/child_process.ProcessRequest) -> boolReports whether the request selects a working directory instead of inheriting one.
Exited
pub struct ExitedA completed child process that returned an exit code and two owned captures.
Field code
pub code: i32The code the child returned. Any value, including a nonzero one, is ordinary data.
Field output
pub output: BytesThe complete captured standard output, owned by this outcome.
Field errors
pub errors: BytesThe complete captured standard error, owned by this outcome.
Signaled
pub struct SignaledA completed child process that a signal terminated, with two owned captures.
Field signal
pub signal: i32The platform signal number that terminated the child.
Field output
pub output: BytesThe complete captured standard output, owned by this outcome.
Field errors
pub errors: BytesThe complete captured standard error, owned by this outcome.
ProcessOutcome
pub struct ProcessOutcomeOne completed child execution, represented as an exit or signal termination.
Details
An exit code and a terminating signal are distinct members rather than one integer, so a caller can never read a signal number as though it were an exit code.
Field value
pub value: silk/child_process.Exited | silk/child_process.SignaledThe completed outcome.
ChildProcess
pub service ChildProcessA portable blocking child-process service with complete output capture.
Details
execute closes child standard input and blocks until termination. The returned outcome owns
complete standard-output and standard-error captures. A nonzero exit code is outcome data.
A start, wait, or capture failure produces ProcessError. Owning either capture can also
produce OutOfMemoryError. The operation needs exclusive provider and allocator requirements.
Operation execute
effect fn execute(request: &silk/child_process.ProcessRequest) -> ProcessOutcome ! ProcessError | OutOfMemoryError ? &mut ChildProcess | &mut AllocatorRuns one request to termination and returns owned captures of both output streams.
Details
This operation blocks and closes the child's standard input. A nonzero exit code returns on
the success channel. Start, wait, and capture failures produce ProcessError.
exited
pub fn exited(code: i32, output: Bytes, errors: Bytes) -> ProcessOutcomeCreates an exited outcome that owns output and errors.
signaled
pub fn signaled(signal: i32, output: Bytes, errors: Bytes) -> ProcessOutcomeCreates a signaled outcome that owns output and errors.
isSignaled
pub fn isSignaled(outcome: &silk/child_process.ProcessOutcome) -> boolReports whether a signal terminated the child instead of an exit code.
exitCode
pub fn exitCode(outcome: &silk/child_process.ProcessOutcome) -> Option<i32>Returns the exit code, or None when a signal terminated the child.
terminatingSignal
pub fn terminatingSignal(outcome: &silk/child_process.ProcessOutcome) -> Option<i32>Returns the terminating signal number, or None when the child returned an exit code.
outputBytes
pub fn outputBytes(outcome: &silk/child_process.ProcessOutcome) -> &[u8]Borrows the complete captured standard output without consuming the outcome.
errorBytes
pub fn errorBytes(outcome: &silk/child_process.ProcessOutcome) -> &[u8]Borrows the complete captured standard error without consuming the outcome.
submit
pub effect fn submit(request: &silk/child_process.ProcessRequest) -> ProcessOutcome ! ProcessError | OutOfMemoryError ? &mut ChildProcess | &mut AllocatorRuns the active ChildProcess provider for one request.
Details
This wrapper preserves the service contract: it blocks, closes child input, and returns complete owned captures. It requires exclusive child-process and allocator providers.