#Console
Status: standard library specification draft.
This chapter defines the part of std.console that lib/std writes in
ordinary hd over the language tier:
print, which writes text without adding a newline;eprintln, which writes a line to standard error;- when the host's console output becomes visible;
- the host capability trait
ConsoleInput, and the helpersread_line!andread_all!; - the traits of
ConsoleError; - the test providers
MemoryConsoleandScriptedInput.
The language tier keeps what the prelude and the conformance harness name (Console):
| Item | Why it stays in the language tier |
|---|---|
Console, ConsoleError |
prelude names, and Console is in every default profile (module.profile.default) |
println |
a prelude function that the conformance harness uses |
#Printing Without A Newline
print writes Display text without adding a newline:
use std.console.print
pub fn main() -> void $ Console:
print("ready: ")
println(1)Open in playground- std-console.print.decl
std.consoledeclarespub fn print[T < Display](value: T) -> void $ Console. - std-console.print.one-write
print(value)calls the covering provider'swrite!once withvalue.to_string(), adding nothing. - std-console.print.drive
printdrives its write withblock_on, following its forbidden contexts and active-driver behavior. - std-console.print.errorIf the write returns
.Err,printpanics. Panic:explicit-panic. - std-console.print.cancelledIf
block_onreturns.Cancelledor.Abandoned,printreturns without a panic. - std-console.print.rowEach call requires a
Consolerequirement row or a lexical provider scope. Error:missing-requirement.
#Standard Error
eprintln writes a line to standard error, as println writes one to
standard output:
use std.console.eprintln
fn warn(message: string) -> void $ Console:
eprintln("warning: ${message}")- std-console.eprintln.decl
std.consoledeclarespub fn eprintln[T < Display](value: T) -> void $ Console. Code imports it, as inuse std.console.eprintln. - std-console.eprintln.one-write
eprintln(value)calls the covering provider'swrite_error!once withvalue.to_string()followed by"\n". - std-console.eprintln.drive-write
eprintlndrives that call asprintlndriveswrite!, with the same rules:block_ondriving, its forbidden contexts, and anexplicit-panicon.Err. - std-console.eprintln.rowEach
eprintlncall must be covered by aConsolerequirement row or a lexical provider scope. Error:missing-requirement.
use std.console.eprintln
pub fn main() -> void:
eprintln("no console") # error: missing-requirementOpen in playgroundWhy. Standard error is a second stream of the same console, so it is a second method of
Console, not a second trait. A program that prints needs one requirement for both streams.
#Host Output
The host's Console provider writes each line out before the program
waits on the host:
use std.console.eprintln
use std.time.{Clock, sleep}
fn main!() -> void $ Console + Clock:
println("working") # visible before the sleep starts
sleep!(2s)
eprintln("done") # after "working", on standard errorOpen in playground- std-console.host.visible-textEach completed
write!orwrite_error!of the host'sConsoleprovider reaches its host stream before the program next waits on the host or ends. - std-console.host.chunk-orderThe host's
Consoleprovider keeps standard output and standard error in their relative order: their chunks reach the host in call order.
Note. So a
printlnoreprintlnline is visible before the next host wait, such as asleep!or asend!.
Why. A program that prints progress and then waits on a slow request shows the progress first.
#Console Input
ConsoleInput reads standard input one line at a time:
pub trait ConsoleInput:
fn read_line!(&mut self) -> Result[string?, ConsoleError]- std-console.input.decl
std.consoledeclares the host capability traitConsoleInputwith the method above. Code imports it, as inuse std.console.ConsoleInput. - std-console.input.read-line
read_line!returns the next line of input without its line ending, or.Noneat the end of input. - std-console.input.suspendsConsole input is I/O, so
read_line!is a bang call. - std-console.input.mut-ref.reference-values
read_line!takes&mut self, soConsoleInputis a mutable requirement trait,$.use(ConsoleInput)is a&mut dyn ConsoleInputbyreq.ref.use.reference-values, and a provider may advance through its input. - std-console.input.helper
std.consoledeclarespub fn read_line!() -> Result[string?, ConsoleError] $ ConsoleInput, which callsread_line!on theConsoleInputprovider that covers the call.
use std.console.{ConsoleInput, read_line}
fn first_line!() -> string $ ConsoleInput:
match read_line!():
.Ok(.Some(line)) => line
_ => ""read_all! reads the rest of the input in one call, for a filter such as
cat orders.json | total:
use std.console.{ConsoleInput, read_all}
fn whole_input!() -> string $ ConsoleInput:
match read_all!():
.Ok(text) => text
.Err(_) => ""- std-console.input.read-all-ref
ConsoleInputalso declaresfn read_all!(&mut self) -> Result[string, ConsoleError], a bang call that takes&mut selfasread_line!does. It returns the rest of the input as one string with the line structure of the text, as Go'sio.ReadAll, Rust'sread_to_stringand Python'sread()do:"a\nb\n"gives"a\nb\n","a\nb"gives"a\nb", and"\n"gives"\n". A\r\nis read as\n, asread_line!reads it. The result is""when no input is left. After it,read_line!returns.Ok(.None). - std-console.input.read-all-default
read_all!has a default body, so a provider need not define it. The body callsread_line!until it returns.Ok(.None), appends each line and a\nto oneStringBuilder, and returns the first.Errthatread_line!returns. Sinceread_line!drops each line ending, the body cannot tell whether the last line had one: it approximates by ending every line with\n. - std-console.input.read-all-hostThe host's
ConsoleInputprovider overridesread_all!with one read of the rest of standard input. Its result is the input as read, with each\r\nread as\nand nothing else changed; bytes that are not UTF-8 become U+FFFD. It returns.Err(ConsoleError.Closed)when standard input cannot be read. - std-console.input.read-all-helper
std.consoledeclarespub fn read_all!() -> Result[string, ConsoleError] $ ConsoleInput, which callsread_all!on theConsoleInputprovider that covers the call. A typed read isdecode::[T](read_all!()?).
Why. Input is its own trait, apart from
Console, so a program that only prints gains no authority to read.
#Console Errors
ConsoleError is a prelude enum with closed, denied, and other failures:
fn closed(error: ConsoleError) -> bool:
error == ConsoleError.Closed- std-console.error.traits
ConsoleErrorimplementsEq,Debug, andDisplay. ItsDisplaytext forClosedisconsole closed. - std-console.error.debug-payloadsIts
Debugwrites the qualified variant name and named payloads, includingConsoleError.NotGranted(subject="stdout"). - std-console.error.payload-display
NotGranted(subject)displaysconsole access to SUBJECT is not granted;Other(message)displays its message. - std-console.error.payload-equalityEquality compares the variant and its payload.
- std-console.error.error
ConsoleErrorimplementsstd.error.Error, as Standard Error Types requires.
#Buffer Console
MemoryConsole is the recording Console provider for tests:
use std.console.MemoryConsole
fn greet(name: string) -> void $ Console:
println("hello, ${name}")
tests:
use std.testing.assert_equal
it("records the line"):
console := MemoryConsole::new()
$.with(Console=console):
greet("Ada")
assert_equal(console.output(), ["hello, Ada", "\n"], reason="text and newline writes")- std-console.buffer.decl
std.consoledeclaresMemoryConsole, which implementsConsoleandDebug, with private fields. Code imports it, as inuse std.console.MemoryConsole. - std-console.buffer.install-ref
MemoryConsole::new() -> *mut MemoryConsolereturns a new object holding a console that has recorded nothing.MemoryConsoleis a struct used behind that reference, and it implementsConsole's&mut selfmethods. So installing it asConsole=consoleshares the object, byreq.ref.install-ref, and the code that installed it reads what was written. - std-console.buffer.output-text
c.output() -> List[string]returns eachwrite!text unchanged, in call order, including any newline supplied by the caller. - std-console.buffer.error-text
c.error_output() -> List[string]returns eachwrite_error!text unchanged, in call order, including any newline supplied by the caller.MemoryConsoleoverrideswrite_error!, so error lines are not inoutput(). - std-console.buffer.never-failsEvery write returns
.Ok(()).
Why. A test checks what a program printed and what it reported apart, as Go's
bytes.Bufferpair forStdoutandStderrlets it.
#Scripted Input
ScriptedInput is the deterministic ConsoleInput provider:
use std.console.{ConsoleInput, ScriptedInput, read_line}
fn first_line!() -> string $ ConsoleInput:
match read_line!():
.Ok(.Some(line)) => line
_ => ""
tests:
use std.testing.assert
it("reads the scripted line"):
input := ScriptedInput::new(["yes"])
$.with(ConsoleInput=input):
assert(first_line!() == "yes", reason="the first line")- std-console.scripted.decl
std.consoledeclaresScriptedInput, which implementsConsoleInputandDebug, with private fields. Code imports it, as inuse std.console.ScriptedInput. - std-console.scripted.new-ref
ScriptedInput::new(lines: List[string]) -> *mut ScriptedInputreturns a new object holding a provider that has read none oflines. Installing it shares the object, so later reads continue where earlier ones stopped. - std-console.scripted.readEach
read_line!returns.Ok(.Some(line))for the next unread line, in order. - std-console.scripted.endOnce every line is read,
read_line!returns.Ok(.None), on every later call too. - std-console.scripted.read-all
ScriptedInputoverridesread_all!: it returns each unread line followed by\n, then has read every line. A scripted line has no ending of its own, so this is the text a host gives for input whose every line ends. - std-console.scripted.no-hostA
ScriptedInputreads nothing from the host's standard input.
Why. Every host capability trait has a deterministic provider beside it, so a test never touches the host.
See also: Console, Host Capabilities.