Silk

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 ProcessOperation

A provider-reported execution stage for one ProcessError.

Field code

pub code: i32

The stable numeric code for the provider-reported execution stage.

ProcessReason

pub struct ProcessReason

A portable recovery category for one ProcessError.

Field code

pub code: i32

The stable numeric code for the portable recovery category.

ProcessError

pub struct ProcessError

A 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: ProcessOperation

The execution stage reported by the provider.

Field reason

pub reason: ProcessReason

The portable category that callers can use for recovery.

spawnOperation

pub fn spawnOperation() -> ProcessOperation

Returns the stage for preparing and starting the child process.

waitOperation

pub fn waitOperation() -> ProcessOperation

Returns the stage for waiting until the child process terminates.

captureOperation

pub fn captureOperation() -> ProcessOperation

Returns the stage for copying the completed output and error streams.

operationCode

pub fn operationCode(operation: ProcessOperation) -> i32

Returns the stable numeric code for an execution stage.

notFound

pub fn notFound() -> ProcessReason

Returns the reason used when no executable exists at the requested path.

permissionDenied

pub fn permissionDenied() -> ProcessReason

Returns the reason used when the provider denies access to the requested executable.

invalidRequest

pub fn invalidRequest() -> ProcessReason

Returns the reason used when the provider cannot present the request to its process boundary.

noSpace

pub fn noSpace() -> ProcessReason

Returns the reason used when process setup or captured output exhausts provider storage.

unsupported

pub fn unsupported() -> ProcessReason

Returns the reason used when the provider does not support the requested process operation.

other

pub fn other() -> ProcessReason

Returns the reason used when no other portable recovery category applies.

reasonCode

pub fn reasonCode(reason: ProcessReason) -> i32

Returns the stable numeric code for a portable recovery category.

failure

pub fn failure(operation: ProcessOperation, reason: ProcessReason) -> ProcessError

Creates a process failure without a provider-defined numeric code.

failureWithCode

pub fn failureWithCode(operation: ProcessOperation, reason: ProcessReason, code: i32) -> ProcessError

Creates 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 ProcessRequest

One 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 Allocator

Creates 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 Allocator

Creates 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 Allocator

Appends 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 Allocator

Appends 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) -> usize

Returns 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) -> usize

Returns 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) -> bool

Reports whether the request selects a working directory instead of inheriting one.

Exited

pub struct Exited

A completed child process that returned an exit code and two owned captures.

Field code

pub code: i32

The code the child returned. Any value, including a nonzero one, is ordinary data.

Field output

pub output: Bytes

The complete captured standard output, owned by this outcome.

Field errors

pub errors: Bytes

The complete captured standard error, owned by this outcome.

Signaled

pub struct Signaled

A completed child process that a signal terminated, with two owned captures.

Field signal

pub signal: i32

The platform signal number that terminated the child.

Field output

pub output: Bytes

The complete captured standard output, owned by this outcome.

Field errors

pub errors: Bytes

The complete captured standard error, owned by this outcome.

ProcessOutcome

pub struct ProcessOutcome

One 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.Signaled

The completed outcome.

ChildProcess

pub service ChildProcess

A 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 Allocator

Runs 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) -> ProcessOutcome

Creates an exited outcome that owns output and errors.

signaled

pub fn signaled(signal: i32, output: Bytes, errors: Bytes) -> ProcessOutcome

Creates a signaled outcome that owns output and errors.

isSignaled

pub fn isSignaled(outcome: &silk/child_process.ProcessOutcome) -> bool

Reports 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 Allocator

Runs 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.

On this page