Silk

silk/standard_input

Portable blocking reads of raw standard-input bytes through an explicit provider.

When to use

Require StandardInput for byte-oriented input that should be replaceable in tests. Decode text separately so invalid input can be handled without losing its original bytes.

Details

Each read writes only a prefix of the caller's mutable buffer and reports the exact committed count, which may be smaller than the buffer length. For valid non-empty reads, EndOfInput is ordinary outcome data and promises that no later read will produce bytes. StreamReadError is reserved for a provider that could not complete a read.

This service is separate from StandardStreams: output commits a complete message or fails, while input is inherently partial and eventually ends.

Gotchas

The caller must supply a non-empty read buffer. A zero-length native read cannot distinguish available input from end-of-input.

Examples

Read one byte and then observe the end of input

import silk.effect { Effect }

import silk.standard_input as Input

import silk.u8 as u8

import silk.usize as usize

struct OneByte {
  complete: bool
}

effect fn read(self: &mut OneByte, buffer: &mut [u8]) -> Input.ReadOutcome
! Input.StreamReadError {
  if self.complete {
    return Input.endOfInput()
  }
  buffer[usize.ZERO] = u8.toU8(42)
  self.complete = true
  return Input.filled(usize.ONE)
}

impl Input.StandardInput for OneByte {
  read: OneByte.read
}

effect fn program() -> i32
! Input.StreamReadError {
  let mut provider = OneByte {complete: false}
  let mut buffer = [u8.toU8(0)]
  let first = run Input.receive(&mut buffer)
    |> Effect.provideMut<Input.StandardInput>(&mut provider)
  if Input.count(&first) != usize.ONE {
    return 1
  }
  let second = run Input.receive(&mut buffer)
    |> Effect.provideMut<Input.StandardInput>(&mut provider)
  if Input.isEndOfInput(&second) == false {
    return 2
  }
  return u8.toI32(buffer[usize.ZERO])
}

effect fn recover(error: Input.StreamReadError) -> i32 {
  return 0
}

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

Import as StandardInput with import silk.standard_input.

Public declarations: 11.

StreamReadError

pub struct StreamReadError

A typed failure from a standard-input provider that could not complete one read.

readFailure

pub fn readFailure() -> StreamReadError

Creates a standard-input failure for a provider that cannot complete one read.

Filled

pub struct Filled

A read that committed a known prefix of the caller's buffer.

Field count

pub count: usize

The exact number of leading bytes written, which can be less than the buffer length.

EndOfInput

pub struct EndOfInput

A valid non-empty read that observed permanent end-of-input without changing the buffer.

ReadOutcome

pub struct ReadOutcome

One completed read, represented as committed bytes or permanent end-of-input.

Details

End-of-input is outcome data instead of failure. Filled identifies the exact initialized prefix; bytes after that prefix retain their previous values.

Field value

pub value: silk/standard_input.EndOfInput | silk/standard_input.Filled

The committed-byte count or permanent end-of-input result.

StandardInput

pub service StandardInput

A portable blocking service for partial reads of raw standard-input bytes.

When to use

Use this service for replaceable byte input. Decode text in a separate operation after each read when text validation is required.

Details

read changes only the reported leading prefix of the buffer. The committed count can be less than the buffer length. A host error produces StreamReadError; end-of-input does not.

After EndOfInput from a valid non-empty read, later reads do not produce bytes.

Operation read

effect fn read(buffer: &mut [u8]) -> ReadOutcome ! StreamReadError ? &mut StandardInput

Reads at most the buffer length and reports the exact committed prefix or end-of-input.

Details

Bytes after a Filled count keep their previous values. For a valid non-empty buffer, EndOfInput means no later call produces bytes. Provider failure produces StreamReadError.

Gotchas

buffer must be non-empty. A native provider cannot distinguish a zero-capacity read from end-of-input.

filled

pub fn filled(count: usize) -> ReadOutcome

Creates an outcome for a read that committed exactly count leading bytes.

Gotchas

A provider must not use a count greater than the buffer length passed to its read operation.

endOfInput

pub fn endOfInput() -> ReadOutcome

Creates an outcome that reports permanent end-of-input after a valid non-empty read.

count

pub fn count(outcome: &silk/standard_input.ReadOutcome) -> usize

Returns the committed byte count, or zero for end-of-input.

Gotchas

A zero count does not by itself identify end-of-input. Use isEndOfInput when that distinction controls another read.

isEndOfInput

pub fn isEndOfInput(outcome: &silk/standard_input.ReadOutcome) -> bool

Reports whether the outcome guarantees that no later read produces bytes.

receive

pub effect fn receive(buffer: &mut [u8]) -> ReadOutcome ! StreamReadError ? &mut StandardInput

Reads through the active StandardInput provider into a mutable buffer.

Details

This wrapper preserves partial-read behavior and the exclusive provider requirement. Inspect count before reading buffer bytes. Use isEndOfInput to decide when draining is complete.

Gotchas

buffer must be non-empty. A native provider can otherwise report end-of-input while bytes are still available.

On this page