All functions live in namespaces — no flat builtins (decision: FullNamespaceEverywhere).
Standard modules ship as ordinary Aver source embedded in the compiler. Import
them explicitly with depends; they do not depend on the current directory or
--module-root, and project files cannot shadow their reserved names.
module Packet
depends [Bytes, Crypto.Digest32]
fn validate(payload: List<Int>) -> Result<Bytes, String>
Bytes.fromList(payload)
Bytes is an opaque refinement over List<Int> whose values are all in
0..=255. Digest32, imported from Crypto.Digest32, is a nested refinement
requiring exactly 32 bytes.
Both remain ordinary Aver types and retain their invariants in Lean and Dafny
proof export.
Bytes.fromList written against a list literal whose every element is an
integer literal in 0..=255 cannot fail, so it types as plain Bytes — no
?, no match:
payload = Bytes.fromList([249, 190, 180, 217]) -- : Bytes
Tcp.sendBytes("127.0.0.1", 9, payload)
Anything else keeps Result<Bytes, String>: a variable, a computed list, a
computed element, or a literal outside 0..=255. See
language.md for the exact boundary.
| Function | Signature | Notes |
|---|---|---|
Bytes.fromList |
List<Int> -> Result<Bytes, String> |
Validates every octet; Result.Err names the offending value and its index. An all-literal in-range list argument discharges to plain Bytes — see below |
Bytes.toList |
Bytes -> List<Int> |
Exposes validated values |
Bytes.fromHex |
String -> Result<Bytes, String> |
Even length, case-insensitive, no 0x prefix |
Bytes.toHex |
Bytes -> String |
Total, lowercase output |
Crypto.Digest32.fromBytes |
Bytes -> Result<Digest32, String> |
Requires exactly 32 bytes |
Crypto.Digest32.toBytes |
Digest32 -> Bytes |
Forgets only the length refinement |
Crypto.Digest32.fromHex |
String -> Result<Digest32, String> |
Hex decode plus exact-length validation |
Crypto.Digest32.toHex |
Digest32 -> String |
Always 64 lowercase characters |
Source: src/types/bool.rs
| Function | Signature | Notes |
|---|---|---|
Bool.or |
(Bool, Bool) -> Bool |
Logical OR |
Bool.and |
(Bool, Bool) -> Bool |
Logical AND |
Bool.not |
Bool -> Bool |
Logical NOT |
Source: src/types/list.rs
List is a recursive structure — use it for sequential processing with prepend, take, drop, and match [h, ..t]. For indexed access, use Vector.
| Function | Signature | Notes |
|---|---|---|
List.len |
List<T> -> Int |
|
List.prepend |
(T, List<T>) -> List<T> |
O(1) prepend |
List.take |
(List<T>, Int) -> List<T> |
First n elements; negative n yields [] |
List.drop |
(List<T>, Int) -> List<T> |
All but first n elements; negative n keeps the original list |
List.concat |
(List<T>, List<T>) -> List<T> |
Concatenates two lists |
List.reverse |
List<T> -> List<T> |
Returns a reversed copy |
List.contains |
(List<T>, T) -> Bool |
Membership check via == |
List.zip |
(List<A>, List<B>) -> List<(A, B)> |
Pairs elements, truncates to shorter list |
Source: src/types/vector.rs
Vector is a persistent indexed sequence — use it for grids, buffers, lookup tables, and anywhere you need O(1) access by index. Backed by Rc<Vec<T>> with copy-on-write: set mutates in place when the vector has a single owner, clones otherwise.
| Function | Signature | Notes |
|---|---|---|
Vector.new |
(Int, T) -> Vector<T> |
Create vector of N elements, all set to default |
Vector.get |
(Vector<T>, Int) -> Option<T> |
O(1) indexed access |
Vector.set |
(Vector<T>, Int, T) -> Option<Vector<T>> |
O(1) COW update; None if out of bounds |
Vector.len |
Vector<T> -> Int |
|
Vector.fromList |
List<T> -> Vector<T> |
Convert list to vector |
List.fromVector |
Vector<T> -> List<T> |
Convert vector to list |
Source: src/types/result.rs + constructors in src/vm/runtime.rs.
| Function | Signature | Notes |
|---|---|---|
Result.Ok |
T -> Result<T, E> |
Constructor |
Result.Err |
E -> Result<T, E> |
Constructor |
Result.withDefault |
(Result<T, E>, T) -> T |
Unwrap Ok or return default |
Source: src/types/option.rs + constructors in src/vm/runtime.rs.
| Function | Signature | Notes |
|---|---|---|
Option.Some |
T -> Option<T> |
Constructor |
Option.None |
Option<T> |
Value (not a function) |
Option.withDefault |
(Option<T>, T) -> T |
Unwrap Some or return default |
Option.toResult |
(Option<T>, E) -> Result<T, E> |
Convert Option to Result |
Source: src/types/int.rs
| Function | Signature |
|---|---|
Int.fromString |
String -> Result<Int, String> |
Int.fromFloat |
Float -> Int |
String.fromInt |
Int -> String |
Float.fromInt |
Int -> Float |
Int.abs |
Int -> Int |
Int.min |
(Int, Int) -> Int |
Int.max |
(Int, Int) -> Int |
Int.mod |
(Int, Int) -> Result<Int, String> |
Int.div |
(Int, Int) -> Result<Int, String> |
Source: src/types/bits.rs
Bits is a namespace, not a type. Its arguments and its results are ordinary mathematical Int values; the namespace only fixes how those integers are read for the duration of one call. There is no bit-vector, no machine word, no Word32/Word64, and no persistent width — Bits.and(6, 3) takes two Ints and gives back an Int.
The reading is infinite two's complement: a non-negative integer has infinitely many leading zeroes, a negative one infinitely many leading ones, and and / or / xor / not operate pointwise on those infinite sequences. That is what makes them total on ℤ without a width to complement against, and it gives Bits.and(-1, x) == x, Bits.or(-1, x) == -1, Bits.xor(-1, x) == Bits.not(x) and Bits.not(x) == -x - 1.
Fixed-width behaviour is always requested explicitly, through Bits.low. Arithmetic on Int itself still never overflows and never wraps: Bits.shiftLeft(1, 100) is 1267650600228229401496703205376, not 0.
| Function | Signature |
|---|---|
Bits.and |
(Int, Int) -> Int |
Bits.or |
(Int, Int) -> Int |
Bits.xor |
(Int, Int) -> Int |
Bits.not |
Int -> Int |
Bits.shiftLeft |
(Int, Int) -> Result<Int, String> |
Bits.shiftRight |
(Int, Int) -> Result<Int, String> |
Bits.low |
(Int, Int) -> Result<Int, String> |
For a non-negative n and width:
Bits.shiftLeft(x, n)isx * 2^nBits.shiftRight(x, n)isfloor(x / 2^n)— an arithmetic right shift, soBits.shiftRight(-3, 1) == -2Bits.low(x, width)isx mod 2^width— the non-negative value of the lowestwidthbits, soBits.low(257, 8) == 1,Bits.low(-1, 8) == 255, andBits.low(x, 0) == 0
A negative shift count or width is Result.Err — never a panic, never a silent direction flip, never a clamp. Like Int.div / Int.mod, a syntactic non-negative integer literal count discharges that error at compile time, so Bits.low(x, 32) types as plain Int while Bits.low(x, width) keeps Result<Int, String>.
Prefer Bits.low over a magic mask — it states the protocol invariant instead of implying it:
top = Bits.shiftRight(checksum, 25)
shifted = Bits.shiftLeft(Bits.low(checksum, 25), 5)
mixed = Bits.xor(shifted, value)
Source: src/types/float.rs
| Function | Signature |
|---|---|
Float.fromString |
String -> Result<Float, String> |
Float.fromInt |
Int -> Float |
String.fromFloat |
Float -> String |
Float.abs |
Float -> Float |
Float.floor |
Float -> Int |
Float.ceil |
Float -> Int |
Float.round |
Float -> Int |
Float.min |
(Float, Float) -> Float |
Float.max |
(Float, Float) -> Float |
Float.sin |
Float -> Float |
Float.cos |
Float -> Float |
Float.sqrt |
Float -> Float |
Float.pow |
(Float, Float) -> Float |
Float.atan2 |
(Float, Float) -> Float |
Float.pi |
() -> Float |
Source: src/types/string.rs
| Function | Signature | Notes |
|---|---|---|
String.len |
String -> Int |
Number of characters (Unicode scalar values), on every backend |
String.byteLength |
String -> Int |
UTF-8 byte count |
String.charAt |
(String, Int) -> Option<String> |
Character at character index, or Option.None on out-of-bounds |
String.startsWith |
(String, String) -> Bool |
|
String.endsWith |
(String, String) -> Bool |
|
String.contains |
(String, String) -> Bool |
|
String.slice |
(String, Int, Int) -> String |
Character indices; out-of-range ends clamp |
String.trim |
String -> String |
|
String.split |
(String, String) -> List<String> |
|
String.replace |
(String, String, String) -> String |
|
String.join |
(List<String>, String) -> String |
|
String.chars |
String -> List<String> |
Splits into characters (Unicode scalar values) |
String.fromInt |
Int -> String |
|
String.fromFloat |
Float -> String |
|
String.fromBool |
Bool -> String |
|
String.toLower |
String -> String |
Unicode-aware lowercase |
String.toUpper |
String -> String |
Unicode-aware uppercase |
Source: src/types/map.rs
| Function | Signature | Notes |
|---|---|---|
{} (literal) |
— | The empty map; type from context (annotation or expected type). No Map.empty() builtin since 0.17 — symmetric with [] for List. |
Map.fromList |
List<(K, V)> -> Map<K, V> |
The key type must order; Float, Map and Vector cannot key a map |
Map.set |
(Map<K, V>, K, V) -> Map<K, V> |
Returns new map with key set |
Map.get |
(Map<K, V>, K) -> Option<V> |
|
Map.has |
(Map<K, V>, K) -> Bool |
|
Map.remove |
(Map<K, V>, K) -> Map<K, V> |
Returns new map without key |
Map.keys |
Map<K, V> -> List<K> |
|
Map.values |
Map<K, V> -> List<V> |
|
Map.entries |
Map<K, V> -> List<(K, V)> |
|
Map.len |
Map<K, V> -> Int |
Sets: Map<T, Unit> is the Aver way to have a set — see language.md for usage and codegen lowering.
Source: src/types/char.rs — not a type, operates on String/Int.
| Function | Signature | Notes |
|---|---|---|
Char.toCode |
String -> Int |
Unicode scalar value of first char |
Char.fromCode |
Int -> Option<String> |
Code point to 1-char string, Option.None for surrogates/invalid |
Source: src/types/crypto.rs; byte and digest types come from the embedded
Bytes and Crypto.Digest32 Aver modules.
| Function | Signature | Notes |
|---|---|---|
Crypto.sha256 |
Bytes -> Digest32 |
Pure, total SHA-256 over validated bytes. |
Import both nominal types with depends [Bytes, Crypto.Digest32]. Hashing is
deterministic and total over Bytes, so it requires neither an effect declaration
nor a Result:
fn doubleSha(bytes: Bytes) -> Digest32
first = Crypto.sha256(bytes)
Crypto.sha256(Crypto.Digest32.toBytes(first))
Namespace effect shorthand: declaring ! [ServiceName] covers all methods of that service. For example, ! [Disk] covers the complete text, binary, metadata, and directory API listed below. You can still use granular declarations like ! [Disk.readBytesAt] when you want to be precise. aver check suggests narrowing when a shorthand could be more specific.
The namespaces below are supplied by Aver's standard host runtime. A project can describe an additional host boundary as a capability module: its operations participate in the same effect, Oracle, hostile-testing, proof-trust, provider, and replay machinery. Rust embedders can install one typed in-process ProviderBinding unchanged in either the VM or a generated Rust artifact. On wasip2, complete custom contracts containing only Unit, Bool, Float, and String cross as host-bound generated WIT imports; the component host, not Aver, supplies their implementation. Bare wasm-gc custom adapters remain unavailable and fail closed with a target-specific unsupported(reason) row. aver capabilities FILE shows the complete VM/Rust/wasm-gc/wasip2 matrix. recorded or suppressed replay can run without a live provider.
Source: src/services/args.rs
| Function | Signature | Notes |
|---|---|---|
Args.get |
() -> List<String> |
Command-line arguments passed after -- |
Usage: aver run file.av -- arg1 arg2 arg3
fn main() -> Unit
! [Args.get, Console.print]
args = Args.get()
Console.print(args)
Source: src/services/console.rs
| Function | Signature |
|---|---|
Console.print |
T -> Unit |
Console.error |
T -> Unit (writes to stderr) |
Console.warn |
T -> Unit (writes to stderr) |
Console.readLine |
() -> Result<String, String> |
Source: src/services/http.rs
| Function | Signature | Notes |
|---|---|---|
Http.get |
String -> Result<HttpResponse, String> |
|
Http.head |
String -> Result<HttpResponse, String> |
headers only, body is ""; requests the identity encoding so content-length describes the resource |
Http.delete |
String -> Result<HttpResponse, String> |
|
Http.post |
(String, String, String, Map<String, List<String>>) -> Result<HttpResponse, String> |
url, body, content-type, headers |
Http.put |
(String, String, String, Map<String, List<String>>) -> Result<HttpResponse, String> |
|
Http.patch |
(String, String, String, Map<String, List<String>>) -> Result<HttpResponse, String> |
HttpResponse record: { status: Int, body: String, headers: Map<String, List<String>> }. Headers are a multimap — a single name can carry multiple values (Set-Cookie, Vary, …).
A response that by definition carries no body — any Http.head response, a 204 No Content, a 304 Not Modified — arrives with body: "" and its header fields intact. Every other response is read to the end, so a server that closes before sending the Content-Length it announced is an error, not a short body.
Source: src/services/http_server.rs
| Function | Signature |
|---|---|
HttpServer.listen |
(Int, Fn(HttpRequest) -> HttpResponse ! [...method-level effects...]) -> Unit |
HttpServer.listenWith |
(Int, T, Fn(T, HttpRequest) -> HttpResponse ! [...method-level effects...]) -> Unit |
HttpServer.listen and HttpServer.listenWith accept top-level function values. listenWith is the preferred form when a handler needs configuration, connections, or other app state, because the context stays explicit instead of being hidden in closure capture.
This is the main intended use of function values in Aver: named handlers and callbacks with explicit types and explicit effects. Most user code still stays first-order.
The handler itself still uses exact method-level effects such as Http.get, Tcp.readLine, or Console.print. The server call does not widen those into namespace-level grants.
HttpRequest record: { method: String, path: String, body: String, headers: Map<String, List<String>> }.
HttpResponse record: { status: Int, body: String, headers: Map<String, List<String>> }.
Header keys are case-insensitive by convention (the runtime normalises incoming names to lowercase; outgoing should match).
The caller declares only HttpServer.listen / HttpServer.listenWith. The handler carries its own ! [...] declaration; its effects are checked on the handler function itself rather than copied onto the caller.
Contract source: stdlib/capabilities/disk.av. Native VM and generated Rust share the aver-rt Disk provider; wasm-gc keeps the existing aver.disk_* host imports and wasip2 keeps its WASI filesystem lowering. Signatures, Oracle classification, hostile profiles, replay semantics, and target accounting all derive from the same contract and model hashes. aver.toml path policy is enforced before the provider boundary on native targets.
| Function | Signature | Notes |
|---|---|---|
Disk.readText |
String -> Result<String, String> |
|
Disk.writeText |
(String, String) -> Result<Unit, String> |
path, content |
Disk.appendText |
(String, String) -> Result<Unit, String> |
|
Disk.readBytes |
String -> Result<Bytes, String> |
Reads the whole file as exact octets |
Disk.readBytesAt |
(String, Int, Int) -> Result<Bytes, String> |
path, offset, maximum length; EOF returns a shorter Bytes value |
Disk.writeBytes |
(String, Bytes) -> Result<Unit, String> |
Replaces the file with exact octets |
Disk.appendBytes |
(String, Bytes) -> Result<Unit, String> |
Appends exact octets |
Disk.size |
String -> Result<Int, String> |
File length in bytes |
Disk.exists |
String -> Bool |
|
Disk.delete |
String -> Result<Unit, String> |
Files only |
Disk.deleteDir |
String -> Result<Unit, String> |
Recursive |
Disk.listDir |
String -> Result<List<String>, String> |
|
Disk.makeDir |
String -> Result<Unit, String> |
Creates parents |
Disk.readBytesAt is a single positional effect. It reads at most the requested length, returns Ok(Bytes.fromList([])) when the offset is at or beyond EOF, and rejects negative offsets or lengths. Reading a whole file remains a separate Disk.readBytes effect, so callers do not need the racy size-then-readBytesAt sequence.
Contract source: stdlib/capabilities/tcp.av. Native VM and generated Rust
share the aver-rt Tcp provider; wasm-gc and wasip2 bind their existing host
lowerings to the same exact contract. Signatures, resource ownership,
Oracle classification, hostile profiles, replay semantics, and target
accounting derive from that source contract.
One-shot (stateless):
| Function | Signature |
|---|---|
Tcp.send |
(String, Int, String) -> Result<String, String> |
Tcp.sendBytes |
(String, Int, Bytes) -> Result<Bytes, String> |
Tcp.ping |
(String, Int) -> Result<Unit, String> |
Persistent connections:
| Function | Signature | Notes |
|---|---|---|
Tcp.connect |
(String, Int) -> Result<Tcp.Connection, String> |
Provider-owned resource — see below. |
Tcp.writeLine |
(Tcp.Connection, String) -> Result<Unit, String> |
Appends \r\n on the wire. |
Tcp.writeBytes |
(Tcp.Connection, Bytes) -> Result<Unit, String> |
Exact bytes; nothing appended, nothing encoded. |
Tcp.readLine |
Tcp.Connection -> Result<String, String> |
Strips the trailing \r\n; Ok("") on a clean EOF before any byte. |
Tcp.readBytes |
(Tcp.Connection, Int) -> Result<Bytes, String> |
Reads exactly N bytes, no decoding. Short read is an error. |
Tcp.readSome |
(Tcp.Connection, Int) -> Result<Bytes, String> |
Reads 1–N bytes without waiting to fill N; empty Bytes means clean EOF. |
Tcp.poll |
(Map<Int, Tcp.Connection>, Int) -> Result<List<Int>, String> |
Returns the sorted caller IDs ready for readSome; [] means timeout. |
Tcp.close |
Tcp.Connection -> Result<Unit, String> |
Err("tcp: unknown connection ...") on a double-close. |
Tcp.Connection is a capability resource: only the provider can mint one,
while construction, field reads, equality, hashing, and pattern matches are
rejected by the type checker. Native VM and generated Rust carry the provider's
host token inside that resource; backend-specific socket tables and handles
remain implementation details. Aver code can store, pass, and return the token,
but cannot inspect its internals or manufacture a replacement.
Persistent session I/O has deliberately no read or write deadline. A
session operation may therefore wait indefinitely until it completes, reaches
EOF, or gets an actual I/O error. Once readBytes, readSome, readLine,
writeBytes, or writeLine begins touching the socket, any error removes that
connection from the provider pool. A failed exact read may already have
consumed part of a frame, and a failed write may already have sent part of its
payload; allowing a retry on the same handle would silently desynchronise the
protocol. Argument validation happens first, so a negative or oversized
readBytes count or invalid readSome maximum does not poison the connection.
Timeout errors are rendered without platform errno.
Socket establishment and one-shot request calls retain deployment defaults:
[effects.Tcp]
connect_timeout_secs = 5
request_idle_timeout_secs = 30request_idle_timeout_secs applies only to blocking operations within
Tcp.send and Tcp.sendBytes, not to their total wall-clock duration and never
to persistent sessions. Both settings must be positive integers. Effect
sections reject unknown or misplaced keys, so a typo cannot silently select a
default. Native VM, generated Rust, and the in-process wasm-gc host honour the
settings. wasip2 currently uses host-controlled WASI socket timing and emits a
warning when explicit Tcp timeout settings cannot be honoured.
Tcp.send is stateless and ephemeral — it opens a fresh socket, writes the request bytes raw (no \r\n append), shutdown(Write) to signal end-of-request, then reads the peer's response until EOF, capped at 10 MiB. It does not touch the persistent-connection pool, so a program holding 256 live Tcp.connect handles can still issue Tcp.send to another peer. Stream errors (stream-error.last-operation-failed) surface as Result.Err("tcp: stream error"); a clean half-close (stream-error.closed) returns whatever the peer flushed.
Tcp.sendBytes is the byte-clean form of Tcp.send: same socket behaviour, but
the payload and response stay Bytes and no UTF-8 encoding or decoding
happens in either direction. Prefer it for any binary protocol. Tcp.send
decodes the response with String::from_utf8_lossy, which replaces every
non-UTF-8 sequence with U+FFFD — silently, irreversibly, and starting at the
first offending byte — so it is only safe for protocols whose responses are
valid UTF-8 text. Construct payloads with Bytes.fromList or Bytes.fromHex;
invalid octets are rejected at that refinement boundary before TCP is called.
Tcp.readBytes is the byte-clean form of Tcp.readLine, and the only way to
read a fixed number of bytes off a persistent connection. readLine frames on
\n — wrong for length-prefixed protocols, whose payloads carry 0x0A at
arbitrary offsets — and goes through BufRead::read_line, which rejects
non-UTF-8 input outright. readBytes does neither: it reads exactly the
requested count and decodes nothing.
Tcp.poll and Tcp.readSome form the event-loop receive path. The caller owns
the Int keys in the map, so they can also key peer metadata without making the
provider-owned Tcp.Connection resource comparable. poll returns a sorted,
duplicate-free subset of those keys. A key is ready when its connection has
buffered input, the underlying stream reports readability, or EOF/error can be
observed without waiting. Readiness is only a hint: another consumer may race
the read, and the following operation can still fail.
readSome(connection, maxBytes) performs one bounded read and returns as soon
as any bytes are available instead of waiting to fill maxBytes. maxBytes
must be positive and is capped at 10 MiB. Without a preceding poll, it may
wait indefinitely for the first byte. After poll reports the caller's peer
ID, a single-reader loop can call readSome to make progress without falling
back to exact-count blocking. Empty Bytes is reserved for clean EOF.
The returned payload is nominal Bytes; use Bytes.toList only when ordinary
list operations are needed.
A short read is an error rather than a truncated success, because fewer bytes
than a length prefix promised means the peer went away mid-message. The count is
capped at 10 MiB; a negative, oversized, or i64-overflowing count returns
Result.Err rather than trapping. Tcp.readLine is unchanged and remains the
right choice for line-oriented text protocols.
Tcp.writeBytes is the byte-clean form of Tcp.writeLine. writeLine appends
\r\n unconditionally — two bytes that desynchronise a length-prefixed stream —
and its String argument is UTF-8, so a codepoint above 0x7F is re-encoded
into a multi-byte sequence: the single byte 0xF9 cannot be put on the wire at
all. writeBytes writes the nominal Bytes payload exactly as given. Build it
with Bytes.fromList or Bytes.fromHex; an invalid octet returns Result.Err
at that refinement boundary before any wire I/O, so a bad payload never
half-writes. An empty payload is a no-op. Tcp.writeLine is unchanged and
remains right for line-oriented text.
Contract source: stdlib/capabilities/random.av. Native VM and generated Rust share the aver-rt Random provider; wasm-gc keeps the existing aver.random_* imports and wasip2 keeps its WASI random lowering. Signatures, Oracle classification, hostile profiles, replay semantics, and target accounting all derive from the same contract and model hashes.
| Function | Signature | Notes |
|---|---|---|
Random.int |
(Int, Int) -> Int |
Random integer in [min, max] inclusive |
Random.float |
() -> Float |
Random float in [0.0, 1.0) |
Contract source: stdlib/capabilities/time.av. Native VM and generated Rust share the aver-rt Time adapter; wasm-gc uses the existing aver.time_* imports and wasip2 uses WASI clocks/poll. All four bindings are checked/accounted against the same contract and model hashes.
| Function | Signature | Notes |
|---|---|---|
Time.now |
() -> String |
Current UTC timestamp string (...Z) |
Time.unixMs |
() -> Int |
Unix epoch milliseconds |
Time.sleep |
Int -> Unit |
Sleeps current thread for ms, runtime error on negative |
Source: src/services/terminal.rs (requires terminal feature, enabled by default)
| Function | Signature | Notes |
|---|---|---|
Terminal.enableRawMode |
() -> Unit |
Enter raw mode (no line buffering, no echo) |
Terminal.disableRawMode |
() -> Unit |
Leave raw mode |
Terminal.clear |
() -> Unit |
Clear entire screen |
Terminal.moveTo |
(Int, Int) -> Unit |
Move cursor to column x, row y |
Terminal.print |
a -> Unit |
Print at cursor position (no newline) |
Terminal.setColor |
String -> Unit |
Set foreground: "red"/"green"/"yellow"/"blue"/"white"/"cyan"/"magenta"/"black" |
Terminal.resetColor |
() -> Unit |
Reset colors to default |
Terminal.readKey |
() -> Option<String> |
Non-blocking poll: "up"/"down"/"left"/"right"/"esc"/"q"/char or None |
Terminal.size |
() -> Terminal.Size |
Returns Terminal.Size { width: Int, height: Int } |
Terminal.hideCursor |
() -> Unit |
Hide cursor |
Terminal.showCursor |
() -> Unit |
Show cursor |
Terminal.flush |
() -> Unit |
Flush stdout |
Terminal guard: aver run installs a drop guard that restores the terminal (show cursor, reset colors, disable raw mode) even on panic or runtime error.
Source: src/services/env.rs
| Function | Signature | Notes |
|---|---|---|
Env.get |
String -> Option<String> |
Returns Option.None for missing/unreadable variable |
Env.set |
(String, String) -> Unit |
Runtime error on invalid key/value format |
Runtime policy (aver.toml) can restrict allowed keys:
[effects.Env]
keys = ["APP_*", "PUBLIC_*"]