Silk

silk/child_process

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc.

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 { Bytes }

import silk.child_process { ChildProcess, ProcessError, ProcessOutcome, ProcessRequest }

import silk.allocator { Allocator, OutOfMemoryError }

import silk.effect { Effect }

import silk.filesystem { Path, FileError }

import silk.option { Option }

struct Completed {}

effect fn execute(self: &mut Completed, request: &ProcessRequest) -> ProcessOutcome
! ProcessError | OutOfMemoryError
? &mut Allocator {
  return ChildProcess.exited(7, Bytes.make(), Bytes.make())
}

impl ChildProcess for Completed {
  execute: Completed.execute
}

effect fn program() -> i32
! FileError | OutOfMemoryError | ProcessError {
  let mut allocator = Allocator.systemAllocatorProvider()
  let mut provider = Completed {}
  let path = run Path.make("/tool")
    |> Effect.provideMut<Allocator>(&mut allocator)
  let request = run ChildProcess.request(&path)
    |> Effect.provideMut<Allocator>(&mut allocator)
  let outcome = run ChildProcess.submit(&request)
    |> Effect.provideMut<ChildProcess>(&mut provider)
    |> Effect.provideMut<Allocator>(&mut allocator)
  return match move ChildProcess.exitCode(&outcome) {
    Option<i32>.Some {value} => 35 + value
    Option<i32>.None => 1
  }
}

effect fn recover(error: FileError | OutOfMemoryError | ProcessError) -> i32 {
  return 0
}

pub fn main() -> i32 {
  return run Effect.catchAll(program(), recover)
}

Import as ChildProcess with import silk.child_process { ChildProcess }.

Public declarations: 8.

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.

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.

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<'life0> fn execute<'life0>(request: &'life0 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.

Associated function ChildProcess.spawnOperation

pub fn spawnOperation() -> ProcessOperation

Returns the stage for preparing and starting the child process.

Associated function ChildProcess.waitOperation

pub fn waitOperation() -> ProcessOperation

Returns the stage for waiting until the child process terminates.

Associated function ChildProcess.captureOperation

pub fn captureOperation() -> ProcessOperation

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

Associated function ChildProcess.operationCode

pub fn operationCode(operation: ProcessOperation) -> i32

Returns the stable numeric code for an execution stage.

Associated function ChildProcess.notFound

pub fn notFound() -> ProcessReason

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

Associated function ChildProcess.permissionDenied

pub fn permissionDenied() -> ProcessReason

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

Associated function ChildProcess.invalidRequest

pub fn invalidRequest() -> ProcessReason

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

Associated function ChildProcess.noSpace

pub fn noSpace() -> ProcessReason

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

Associated function ChildProcess.unsupported

pub fn unsupported() -> ProcessReason

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

Associated function ChildProcess.other

pub fn other() -> ProcessReason

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

Associated function ChildProcess.reasonCode

pub fn reasonCode(reason: ProcessReason) -> i32

Returns the stable numeric code for a portable recovery category.

Associated function ChildProcess.failure

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

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

Associated function ChildProcess.failureWithCode

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

Creates a process failure with a provider-defined numeric code for diagnostics.

Associated function ChildProcess.providerCode

pub fn providerCode<'life0>(error: &'life0 silk/child_process.ProcessError) -> silk/option.Option<i32>

Returns the provider-defined numeric code, or None when the failure has no such code.

Associated function ChildProcess.request

pub effect<'life0> fn request<'life0>(program: &'life0 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.

Associated function ChildProcess.requestWithin

pub effect<'env> fn requestWithin<'life0: 'env, 'life1: 'env, 'env>(program: &'life0 silk/filesystem.Path, directory: &'life1 silk/filesystem.Path) -> ProcessRequest ! OutOfMemoryError ? &mut Allocator

Creates a request that runs program in directory instead of the caller's working directory.

Associated function ChildProcess.addArgument

pub effect<'env> fn addArgument<'life0: 'env, 'life1: 'env, 'env>(self: &'life0 mut silk/child_process.ProcessRequest, value: &'life1 [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.

Associated function ChildProcess.setVariable

pub effect<'env> fn setVariable<'life0: 'env, 'life1: 'env, 'life2: 'env, 'env>(self: &'life0 mut silk/child_process.ProcessRequest, name: &'life1 [u8], value: &'life2 [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.

Associated function ChildProcess.program

pub fn program<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> &'life0 [u8]

Borrows the executable path bytes for the lifetime of the request borrow.

Associated function ChildProcess.arguments

pub fn arguments<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> &'life0 [u8]

Borrows all ordered arguments as one block of NUL-terminated entries.

Associated function ChildProcess.argumentCount

pub fn argumentCount<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> usize

Returns the number of calls to addArgument that completed successfully.

Associated function ChildProcess.environment

pub fn environment<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> &'life0 [u8]

Borrows the explicit environment as one block of NUL-terminated name=value entries.

Associated function ChildProcess.environmentCount

pub fn environmentCount<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> usize

Returns the number of calls to setVariable that completed successfully.

Associated function ChildProcess.workingDirectory

pub fn workingDirectory<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> &'life0 [u8]

Borrows the selected working-directory bytes, or an empty view when the child inherits one.

Associated function ChildProcess.hasWorkingDirectory

pub fn hasWorkingDirectory<'life0>(self: &'life0 silk/child_process.ProcessRequest) -> bool

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

Associated function ChildProcess.exited

pub fn exited(code: i32, output: Bytes, errors: Bytes) -> ProcessOutcome

Creates an exited outcome that owns output and errors.

Associated function ChildProcess.signaled

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

Creates a signaled outcome that owns output and errors.

Associated function ChildProcess.isSignaled

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

Reports whether a signal terminated the child instead of an exit code.

Associated function ChildProcess.exitCode

pub fn exitCode<'life0>(outcome: &'life0 silk/child_process.ProcessOutcome) -> silk/option.Option<i32>

Returns the exit code, or None when a signal terminated the child.

Associated function ChildProcess.terminatingSignal

pub fn terminatingSignal<'life0>(outcome: &'life0 silk/child_process.ProcessOutcome) -> silk/option.Option<i32>

Returns the terminating signal number, or None when the child returned an exit code.

Associated function ChildProcess.outputBytes

pub fn outputBytes<'life0>(outcome: &'life0 silk/child_process.ProcessOutcome) -> &'life0 [u8]

Borrows the complete captured standard output without consuming the outcome.

Associated function ChildProcess.errorBytes

pub fn errorBytes<'life0>(outcome: &'life0 silk/child_process.ProcessOutcome) -> &'life0 [u8]

Borrows the complete captured standard error without consuming the outcome.

Associated function ChildProcess.submit

pub effect<'life0> fn submit<'life0>(request: &'life0 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

When to useDetailsGotchasExamplesHandle a nonzero child exit as outcome dataProcessOperationField codeProcessReasonField codeProcessErrorDetailsField operationField reasonProcessRequestDetailsGotchasExitedField codeField outputField errorsSignaledField signalField outputField errorsProcessOutcomeDetailsField valueChildProcessDetailsOperation executeDetailsAssociated function ChildProcess.spawnOperationAssociated function ChildProcess.waitOperationAssociated function ChildProcess.captureOperationAssociated function ChildProcess.operationCodeAssociated function ChildProcess.notFoundAssociated function ChildProcess.permissionDeniedAssociated function ChildProcess.invalidRequestAssociated function ChildProcess.noSpaceAssociated function ChildProcess.unsupportedAssociated function ChildProcess.otherAssociated function ChildProcess.reasonCodeAssociated function ChildProcess.failureAssociated function ChildProcess.failureWithCodeAssociated function ChildProcess.providerCodeAssociated function ChildProcess.requestAssociated function ChildProcess.requestWithinAssociated function ChildProcess.addArgumentDetailsGotchasAssociated function ChildProcess.setVariableDetailsGotchasAssociated function ChildProcess.programAssociated function ChildProcess.argumentsAssociated function ChildProcess.argumentCountAssociated function ChildProcess.environmentAssociated function ChildProcess.environmentCountAssociated function ChildProcess.workingDirectoryAssociated function ChildProcess.hasWorkingDirectoryAssociated function ChildProcess.exitedAssociated function ChildProcess.signaledAssociated function ChildProcess.isSignaledAssociated function ChildProcess.exitCodeAssociated function ChildProcess.terminatingSignalAssociated function ChildProcess.outputBytesAssociated function ChildProcess.errorBytesAssociated function ChildProcess.submitDetails