libero/field_type
The structured Gleam type representation libero uses for both shared-type discovery (walker) and handler signature scanning (scanner). Lifting it out of either module lets both produce and consume the same shape, and lets codegen pattern-match structurally instead of re-parsing strings.
Types
A Gleam type, resolved to a structured form. Module-qualified
references (e.g. types.Item written in user code) are resolved
to their canonical module path (e.g. shared/types) at production
time; downstream consumers can rely on module_path being the
import-stable name without re-doing alias resolution.
pub type FieldType {
UserType(
module_path: String,
type_name: String,
args: List(FieldType),
)
ListOf(element: FieldType)
OptionOf(inner: FieldType)
ResultOf(ok: FieldType, err: FieldType)
DictOf(key: FieldType, value: FieldType)
TupleOf(elements: List(FieldType))
IntField
FloatField
StringField
BoolField
BitArrayField
NilField
TypeVar(name: String)
}
Constructors
-
UserType( module_path: String, type_name: String, args: List(FieldType), ) -
ListOf(element: FieldType) -
OptionOf(inner: FieldType) -
-
-
TupleOf(elements: List(FieldType)) -
IntField -
FloatField -
StringField -
BoolField -
BitArrayField -
NilField -
TypeVar(name: String)A type variable (generic parameter) that survives to runtime. Cannot be encoded over the wire; codegen emits a runtime error.
Values
pub fn builtin_field_type(
name name: String,
parameters parameters: List(a),
recurse recurse: fn(a) -> FieldType,
) -> Result(FieldType, Nil)
Map a builtin Gleam type name (and its parameters) to the
corresponding FieldType. Returns Error(Nil) when the name isn’t a
recognised builtin or the parameter arity doesn’t match. The caller
supplies recurse to convert each parameter (typically a
glance.Type) into a FieldType, keeping this module independent of
glance. Used by scanner and walker so the builtin dispatch lives in
one place.
pub const builtin_type_names: List(String)
Names of Gleam types that libero treats as builtin: not user-defined, not requiring atom registration, and not from the shared/ tree. Both the scanner and the walker consult this list, so they agree on what counts as a primitive across the codegen pipeline.
Note: Tuples are not in this list because they are structural types
(glance.TupleType), not named types (glance.NamedType). They’re
handled by direct pattern matching in scanner and walker rather than
through builtin_field_type. The two paths converge on TupleOf.
pub fn collect_user_types(
ft: FieldType,
) -> List(#(String, String))
Walk a FieldType and collect every UserType reference it contains
(including transitive ones). Returns #(module_path, type_name)
tuples in discovery order. Used by codegen to determine which
shared modules to import.
pub fn contains(
ft: FieldType,
predicate: fn(FieldType) -> Bool,
) -> Bool
True if predicate returns True for ft or any FieldType nested
within it. Used by codegen to ask questions like “does this type
transitively contain Option?” without substring-scanning.
pub fn is_builtin(name: String) -> Bool
True if name is one of the builtin Gleam type names libero recognises.
pub fn last_segment(module_path: String) -> String
Last /-separated segment of a module path, or the path itself if
no separator is present. Used wherever codegen needs the “short”
module name for aliases or display.
pub fn to_canonical_token(ft: FieldType) -> String
Render a FieldType to its canonical hash-basis token. Used by
wire_identity.canonical_signature to build the deterministic string
that feeds the wire-identity hash.
The canonical token is purely structural: it captures the type’s shape
in a form that is stable across compilations and across tooling. A
renamed field preserves the token (only types appear), but a retyped
field changes it. UserType references render as <type:module|name>
with no transitive hash, which keeps recursive and mutually-recursive
types finite and side-steps cycle resolution entirely.
Format reference (excerpt from the wire-type-identity spec):
IntField -> “int”
FloatField -> “float”
ListOf(t) -> “list<
TypeVars survive to runtime as encoding errors in real wire types; rendered here as “typevar:name” so the canonical signature stays well-formed even for in-progress type definitions.
pub fn to_gleam_source(ft: FieldType) -> String
Render a FieldType as the user-readable Gleam type syntax it came
from. UserType uses the LAST segment of the module path so the
output matches what the user originally wrote (e.g. types.Item,
not shared/types.Item). Used by codegen to embed types in
generated Gleam source.
pub fn to_gleam_source_with_alias(
ft: FieldType,
resolve_alias: fn(String) -> String,
) -> String
Like to_gleam_source but uses a caller-supplied function to
resolve the import alias for a module path. This allows codegen to
provide disambiguated aliases when multiple modules share the same
last segment (e.g. two different id_ modules).