std.reflect

Read-only inspection of values.

Importuse std.reflect

Types

enum Mirror#

A value, mirrored. mirror produces it; every helper in this module reads it. Sum types stay faithful — some(5) is Some(Int(5)), not Int(5) — and a container that reaches itself mirrors as Ref(path) rather than diverging.

Void

The unit value.

Bool(bool)

A boolean leaf.

Int(int)

An integer leaf.

Float(float)

A float leaf.

Str(string)

A string leaf.

Bytes(Bytes)

A byte buffer leaf.

Arr([Mirror])

Array elements, in order.

Tup([Mirror])

Tuple elements, in declaration order.

Dict([(Mirror, Mirror)])

Dictionary entries as key/value pairs, in insertion order.

Struct(string, [(string, Mirror)])

The declared type name, then its fields as name/value pairs in declaration order.

Variant(string, string, Optional<string>, [Mirror])

The enum's declared name, the variant name, its optional declared string value, then the payload slots in order.

None

An Optional<T> holding nothing.

Some(Mirror)

An Optional<T>'s payload, kept wrapped so absence stays distinguishable.

Ok(Mirror)

A Result's success payload.

Err(string, string)

A failure's error name, then its message.

Fn

A closure — opaque, with nothing to descend into.

Future

A future — opaque.

Chan

A channel — opaque.

Iter

An iterator — opaque, since reading it would consume it.

Ptr

A raw foreign pointer — opaque.

Resource(string)

A native handle, named by its resource kind.

Ref(string)

A back-edge: the path of the enclosing value this one points at, instead of an endless tree.

Constants

const KIND_VOID#

Reflection kind tag for void values.

const KIND_BOOL#

Reflection kind tag for bool values.

const KIND_INT#

Reflection kind tag for int values.

const KIND_FLOAT#

Reflection kind tag for float values.

const KIND_STR#

Reflection kind tag for str values.

const KIND_ARRAY#

Reflection kind tag for array values.

const KIND_DICT#

Reflection kind tag for dict values.

const KIND_STRUCT#

Reflection kind tag for struct values.

const KIND_ENUM#

Reflection kind tag for enum values.

const KIND_NONE#

Reflection kind tag for none values.

const KIND_SOME#

Reflection kind tag for some values.

const KIND_BYTES#

Reflection kind tag for bytes values.

const KIND_TUPLE#

Reflection kind tag for tuple values.

const KIND_OTHER#

Reflection kind tag for other values.

Functions

extern fn mirror<T>(value: T) -> Mirror#
  • value: Any value; runtime values are self-describing, so no type argument is needed.
struct Project { name: string, stars: int, active: bool }

fn main() {
  let value = std.reflect.mirror(
    Project { name: "moon", stars: 42, active: true },
  )
  println(std.reflect.type_name(value))
  println(std.reflect.keys(value))
  println(std.reflect.as_str(std.reflect.get(value, "name").unwrap()).unwrap())
  println(std.reflect.as_int(std.reflect.at(value, 1).unwrap()).unwrap())
  println(std.reflect.length(value))
  println(std.reflect.entries(value).len())
  println(std.reflect.paths(value).len())
  println(std.reflect.has_cycle(value))
}
extern fn walk_begin<T>(value: T) -> bool#

Start a walk at value. Replaces any walk in progress.

  • value: The value to place the cursor on.
struct Point { x: int, y: int }

fn main() {
  std.reflect.walk_begin(Point { x: 3, y: 4 })
  println(std.reflect.kind_name(std.reflect.node_kind()))
  println(std.reflect.node_type_name())
  let mut index = 0
  while index < std.reflect.node_len() {
    println(std.reflect.node_field(index))
    if std.reflect.enter(index) {
      println(std.reflect.node_int())
      std.reflect.leave()
    }
    index += 1
  }
  println(std.reflect.walk_depth())
  std.reflect.walk_end()
}
extern fn walk_end()#

End the walk and drop the cursor.

extern fn walk_depth() -> int#

How deep the cursor is; 0 when no walk is in progress.

extern fn node_kind() -> int#

What the node under the cursor is — one of the KIND_* values above.

extern fn node_len() -> int#

How many children the node has: fields, elements, entries, payload slots. A struct reports its declared arity, so field names and child indices line up even where a value does not carry a field.

extern fn node_int() -> int#

Returns the integer payload of the current reflection node.

extern fn node_float() -> float#

Returns the float payload of the current reflection node.

extern fn node_bool() -> bool#

Returns the Boolean payload of the current reflection node.

extern fn node_str() -> string#

Returns the string payload of the current reflection node.

extern fn node_type_name() -> string#

The declared name for a struct or enum, the primitive spelling otherwise.

extern fn node_variant() -> string#

The variant name of an enum node, or "" for anything else.

extern fn node_field(index: int) -> string#

The i-th field name of a struct node, or "" for anything else.

  • index: Zero-based field position in the struct under the cursor.
extern fn node_has(index: int) -> bool#

Whether the i-th child exists — false past the end, and false for a struct field this value does not carry.

  • index: Zero-based child position to test for.
extern fn enter(index: int) -> bool#

Descend into the i-th child. False (and no descent) when there is none.

  • index: Zero-based child position to descend into.
extern fn enter_kind(index: int) -> int#

Descend into the i-th child and answer its kind, or -1 — without descending — when there is no such child. enter and node_kind in one question: a codec asks that pair for every field of every value, and the walk is a sequence of calls, so asking once is measurably cheaper than asking twice (overhaul plan E3).

  • index: Zero-based child position to descend into.
extern fn enter_key(index: int) -> bool#

Descend into the i-th key of a dict node.

  • index: Zero-based entry position whose key to descend into.
extern fn leave()#

Return to the parent.

extern fn has_codec(type_name: string) -> bool#

Whether type_name has a json.codec<T> declaration at all — the question to ask once per node so the per-field rule lookups can be skipped entirely for a type that has no rules.

  • type_name: The declared type name to look up.
extern fn wire_name(type_name: string, field: string) -> string#

The wire spelling a json.codec<T> declaration gives field of type_name — the field's own name when there is no declaration, and "" when the declaration keeps it off the wire entirely.

  • type_name: The declared type name to look up.
  • field: The field's declared name.
extern fn wire_omit_if_none(type_name: string, field: string) -> bool#

Whether the declaration leaves this field out when it holds none.

  • type_name: The declared type name to look up.
  • field: The field's declared name.
fn kind_name(kind: int) -> string#

The name of a KIND_* value, for diagnostics.

  • kind: One of the KIND_* constants.
extern fn build_begin<T>(dest: [T]) -> bool#

Begin building a value of T into dest, where the finished value is appended. Nothing is published until build_commit.

  • dest: Array the finished value is appended to on commit.
struct User { name: string, age: int, nickname: Optional<string> }

fn main() {
  let users: [User] = []
  std.reflect.build_begin<User>(users)
  std.reflect.set_str(std.reflect.field_index<User>("name"), "moon")
  std.reflect.set_int(std.reflect.field_index<User>("age"), 1)
  std.reflect.set_none(std.reflect.field_index<User>("nickname"))
  std.reflect.build_commit()
  std.reflect.build_error().unwrap()
  println(users[0].name)

  std.reflect.build_value<User>()
  std.reflect.set_value(std.reflect.field_index<User>("name"), "Tour")
  std.reflect.set_int(std.reflect.field_index<User>("age"), 2)
  std.reflect.set_none(std.reflect.field_index<User>("nickname"))
  std.reflect.build_commit()
  println(std.reflect.build_take<User>().unwrap().name)
}
extern fn build_value<T>() -> bool#

Begin building a value of T with no destination; build_take<T>() claims it once build_commit has finished it.

extern fn build_take<T>() -> Result<T, Error>#

Claim the value a destination-less build finished.

extern fn field_index<T>(name: string) -> int#

Where name sits in T's declaration, or -1. Ask once per type; set by index in the loop.

  • name: The field's declared name in T.
extern fn set_value<T>(index: int, value: T) -> bool#

Write an already-built value of any shape — a nested struct, an array, a dictionary, an enum — into the i-th field.

  • index: Zero-based field index in the declaration under construction.
  • value: An already-built value of any shape.
extern fn build_field(index: int) -> bool#

Begin building the i-th field, which must itself be a declared struct. build_commit puts the finished value in that field and returns to the enclosing one — the enter/leave of the read cursor, writing.

  • index: Zero-based field index in the declaration under construction.
extern fn set_int(index: int, value: int) -> bool#

Write the i-th field. An Optional<T> field is wrapped for you, and a whole number reaching a float field converts.

  • index: Zero-based field index in the declaration under construction.
  • value: The integer to write; a float field takes it converted.
extern fn set_float(index: int, value: float) -> bool#

Replaces the current reflection node with a float payload.

  • index: Zero-based field index in the declaration under construction.
  • value: The float to write.
extern fn set_bool(index: int, value: bool) -> bool#

Replaces the current reflection node with a Boolean payload.

  • index: Zero-based field index in the declaration under construction.
  • value: The boolean to write.
extern fn set_str(index: int, value: string) -> bool#

Replaces the current reflection node with a string payload.

  • index: Zero-based field index in the declaration under construction.
  • value: The string to write.
extern fn set_none(index: int) -> bool#

Write none into the i-th field, which must be Optional<T>.

  • index: Zero-based field index in the declaration under construction. The field must be Optional<T>.
extern fn build_commit() -> bool#

Finish the value under construction and publish it. A field that was never written is none where it can be and a failure where it cannot, and a failure ends the whole build rather than leaving the cursor half unwound.

extern fn build_error() -> Result<void, Error>#

The build's first failure as an ordinary error, cleared as it is read, and ok(void) when there has not been one. Every call above answers a bare bool and records here instead of returning a Result, because a Result is a heap object and this surface exists so that a constructed value costs none.

extern fn build_abort()#

Abandon every frame. Nothing built is published.

extern fn build_depth() -> int#

How many values are under construction; 0 when none is.

extern fn build_elem(index: int) -> bool#

Begin one element of an array field, appended to that field's own array. The array is created on the first element and holds its elements flat, so they cost nothing each — and this is the only way to build an array of a struct type the caller cannot name, which is every array a decoder meets.

  • index: Zero-based index of the array field the element is appended to.
extern fn set_enum(index: int, variant: string) -> bool#

Write a payload-free variant of the field's declared enum, named by the wire's own spelling.

  • index: Zero-based field index in the declaration under construction.
  • variant: The variant's own spelling, as the wire gives it.
extern fn build_len() -> int#

How many fields the value under construction declares.

extern fn build_type_name() -> string#

The declared name of the type under construction.

extern fn build_field_name(index: int) -> string#

The i-th field's declared name.

  • index: Zero-based field index in the declaration under construction.
extern fn build_field_kind(index: int) -> int#

The i-th field's declared kind, with Optional unwrapped.

  • index: Zero-based field index in the declaration under construction.
extern fn build_field_optional(index: int) -> bool#

Whether the i-th field is Optional<T>.

  • index: Zero-based field index in the declaration under construction.
extern fn build_field_type_name(index: int) -> string#

The i-th field's declared type name, with Optional unwrapped, or "" where the type has no name of its own — an array, a dictionary, a tuple. Its kind says what it is and build_elem_type_name says what it holds.

  • index: Zero-based field index in the declaration under construction.
extern fn build_elem_kind(index: int) -> int#

What one element of an array field holds, or one value of a dictionary field.

  • index: Zero-based index of the array or dictionary field to inspect.
extern fn build_elem_type_name(index: int) -> string#

The declared type name of an array field's element, or a dictionary field's value.

  • index: Zero-based index of the array or dictionary field to inspect.
fn type_name(m: Mirror) -> string#

The name of the mirrored value's type: a primitive spelling for the scalars, the declared name for a struct or an enum, and the resource kind for a native handle.

  • m: The mirror to name.
fn keys(m: Mirror) -> [string]#

The names a value is keyed by: a struct's fields in declaration order, or a dictionary's string keys in insertion order. Every other kind has no keys and answers [].

  • m: The mirror to read; anything without keys answers [].
fn get(m: Mirror, key: string) -> Optional<Mirror>#

The value stored under key: a struct field by name, or a dictionary entry whose key is that string. none when the mirror has no such key.

  • m: The mirror to read.
  • key: A struct field name, or a string dictionary key.
fn at(m: Mirror, i: int) -> Optional<Mirror>#

The element at position i, counting from zero: an Arr or Tup element, a struct's positional field, a dictionary's positional entry value, or an enum variant's payload slot. none when i is out of range.

  • m: The mirror to read.
  • i: Zero-based position among the children.
fn values(m: Mirror) -> [Mirror]#

Everything one level down: array/tuple elements, struct field values, dictionary entry values, or enum payload slots, in order. [] for a leaf.

  • m: The mirror to read; a leaf answers [].
fn entries(m: Mirror) -> [(string, Mirror)]#

Name/value pairs one level down: a struct's fields in declaration order, or a dictionary's string-keyed entries in insertion order. A dictionary entry whose key is not a string has no name and is left out; values still reaches it positionally.

  • m: The mirror to read; entries with no name are left out.
fn length(m: Mirror) -> int#

How many elements the mirror has one level down. Zero for every leaf, so length never has to be guarded by a kind test.

  • m: The mirror to measure; every leaf answers 0.
fn as_int(m: Mirror) -> Optional<int>#

The integer a leaf holds, or none if it holds something else.

  • m: The mirror to read.
fn as_float(m: Mirror) -> Optional<float>#

The float a leaf holds, or none if it holds something else.

  • m: The mirror to read.
fn as_bool(m: Mirror) -> Optional<bool>#

The boolean a leaf holds, or none if it holds something else.

  • m: The mirror to read.
fn as_str(m: Mirror) -> Optional<string>#

The string a leaf holds, or none if it holds something else.

  • m: The mirror to read.
fn paths(m: Mirror) -> [(string, Mirror)]#

Every leaf in the tree, paired with the path it sits at — the one-call answer to "enumerate keys and values recursively".

Paths use the same grammar the JSON codec's errors do: $ is the root, .name descends a struct field or a string-keyed dictionary entry, [i] descends an array/tuple element, an enum payload slot, or a dictionary entry with a non-string key. Some and Ok are transparent — their payload sits at the same path — so $.nickname names the string inside an Optional<string> exactly as it names a bare one.

  • m: The root of the tree to enumerate.
fn has_cycle(m: Mirror) -> bool#

Does the tree contain a back-edge? mirror is total, so a value that contains itself comes back with a Ref in it rather than failing; this is the caller's way to notice.

  • m: The tree to scan for a Ref back-edge.