Exosphere.Lexicon.Generator (Exosphere v0.3.0)

Copy Markdown View Source

Generates Elixir record modules from parsed lexicon IR.

For each app.bsky.* lexicon, generates one module for its main def plus nested modules for internal #defs reachable from it:

Each generated module has:

  • a struct with snake_cased fields plus an extra map for unknown keys
  • new/1 — build and validate from atom- or string-keyed attrs, returning {:ok, struct} | {:error, [{path, message}]}
  • to_map/1 — encode to wire format (record mains inject "$type"; union encoders inject "$type" into their variants)
  • from_map/1 — decode a wire map (delegates to new/1)
  • type_id/0 — the def's NSID ("app.bsky.feed.post", "…#replyRef", or nil)

Cross-lexicon refs resolve when the target def is itself being generated; unresolved refs degrade to pass-through term() values.

Summary

Functions

Generate all modules for lexicons (as from Parser.parse_dir/1).

Write generated specs under lib_dir (default lib/exosphere), creating directories as needed. Returns the paths written.

Types

spec()

@type spec() :: %{
  module: module(),
  path: String.t(),
  nsid: String.t(),
  def_name: String.t() | nil,
  node: Exosphere.Lexicon.Parser.schema_node(),
  lexicon: Exosphere.Lexicon.Parser.lexicon()
}

Functions

generate(lexicons)

@spec generate(%{required(String.t()) => Exosphere.Lexicon.Parser.lexicon()}) :: [
  %{module: module(), path: String.t(), code: String.t()}
]

Generate all modules for lexicons (as from Parser.parse_dir/1).

Namespace rules: app.bsky.* maps to Exosphere.Bsky.*, com.atproto.* to Exosphere.ATProto.*, community.lexicon.* to Exosphere.Community.*; any other authority maps by its full NSID segments.

Generation starts from every lexicon whose main def is a record or object (XRPC query/procedure/subscription and permission-set defs are parsed but not yet generated), then walks refs transitively across lexicons — including into main-less defs.json lexicons — generating a nested module for each reachable object/record def. Refs to non-object defs (e.g. string enum defs) and to unvendored lexicons degrade to pass-through term() values.

Returns specs (module, path relative to lib/exosphere, formatted code), ordered parents-first.

write!(specs, lib_dir \\ "lib/exosphere")

@spec write!([%{module: module(), path: String.t(), code: String.t()}], Path.t()) :: [
  Path.t()
]

Write generated specs under lib_dir (default lib/exosphere), creating directories as needed. Returns the paths written.