Formatting a bounded report
Use std.report to give a value a human-readable spelling through a writer-generic
formatter. The method borrows its receiver, so an error can be formatted again
or examined afterward.
import std.fmt
import std.report
from std.fmt import Formatter, FormatError
pub enum ReadingError:
Invalid(i64)
pub def format[W](self, output: &mut Formatter[W]) -> Result[(), FormatError]:
match self:
case ReadingError.Invalid(line):
output.text("invalid reading on line ")?
output.integer(*line)
def main() -> Result[(), FormatError | IoError]:
error = ReadingError.Invalid(12)
print(std.report.to_string(&error, 80, 8)?)?
print(std.report.to_string(&error, 80, 8)?)?
Ok(())
invalid reading on line 12
invalid reading on line 12
The method’s [W] lets it write to a string, stderr, or another writer.
The formatter supplies that type, so callers do not pass a type argument.
The public enum and its public format method satisfy the structural
std.fmt.Formattable protocol. A class uses the same visibility and method signature.
std.report.render(&value, &mut output) counts a nesting level and restores it
on success or error. Use it when a formatting method delegates to another value.
An anonymous sum supports the same reporting API when every member satisfies
std.fmt.Formattable. You can keep distinct error types and let the compiler
select the active member’s method:
import std.fmt
import std.report
from std.fmt import Formatter, FormatError
pub enum ReadingError:
Invalid(i64)
pub def format[W](self, output: &mut Formatter[W]) -> Result[(), FormatError]:
match self:
case ReadingError.Invalid(line):
output.text("invalid reading on line ")?
output.integer(*line)
pub enum ConnectionError:
Closed
pub def format[W](self, output: &mut Formatter[W]) -> Result[(), FormatError]:
output.text("connection closed")
def show(error: &(ReadingError | ConnectionError)) -> Result[(), FormatError | IoError]:
print(std.report.to_string(error, 80, 1)?)?
Ok(())
def main() -> Result[(), FormatError | IoError]:
reading: ReadingError | ConnectionError = ReadingError.Invalid(12)
connection: ReadingError | ConnectionError = ConnectionError.Closed
show(&reading)?
show(&connection)?
show(&reading)?
Ok(())
invalid reading on line 12
connection closed
invalid reading on line 12
Dispatch borrows the payload without allocating; string collection still
allocates. The original sum remains usable afterward. render counts one depth
level for the sum, with no extra level for selecting its member. Calling
error.format(&mut output) directly also dispatches, but does not count depth.
All members must have the required public method, even if the value currently
holds a different member. Other protocols are not automatically forwarded.
For stderr, construct std.fmt.stderr_formatter(max_bytes, max_depth) and
call render explicitly. Construction and scalar writes allocate no heap
memory, so simple report methods remain usable when allocation is unavailable.
Add any desired "error: " prefix and final "\n" with output.text yourself;
they count toward the byte limit. User methods can still allocate, so this
guarantee depends on what those methods do.
The collector has the same formatting contract but allocates fallibly:
import std.fmt
import std.report
from std.fmt import FormatError
def main() -> Result[(), FormatError | IoError]:
mut output = std.fmt.string_formatter(4, 0)?
output.text("a")?
match output.text("é🙂"):
case Err(_):
print("too long", output.completed())?
case Ok(_):
pass
output.text("é")?
print(std.fmt.finish(&output)?)?
Ok(())
too long 1
aé
Text checks its whole UTF-8 byte length before writing. Numeric output streams
digits, so a limit can leave part of a number. On stderr failure, inspect
completed() and has_failed(); subsequent writes return OutputClosed without
writing again. A limit error is recoverable, as the example shows.
When main returns Err, its concrete error type selects the reporting path.
A type with this public format method produces a human-readable report with
the prefix error: main returned and a final newline. A sum selects this path
only when all its members support it; other types retain structural reporting.
The automatic reporter allows 16,384 bytes and 64 nesting levels. If formatting
fails after writing part of the message, it appends [report unavailable] and
a newline without trying the method again. Stderr failures stop output.
Use std.fmt for floats, widths,
debug escaping or an arbitrary byte writer. The
reference lists the complete reporting API.