Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Identifier Naming

Schema names are the source of truth. Generated code adapts those names to each target language’s naming style, while runtime data lookup keeps using the original schema names.

For example, a schema field named max_stack may become maxStack in TypeScript, MaxStack in C#, and max_stack in Rust. The generated decoder still reads the field named max_stack from the runtime bundle.

Naming Pipeline

Sora derives common name forms from each schema name before language generation:

FormExample from max_stackCommon use
Rawmax_stackRuntime table names, field names, enum text values, union tags.
PascalMaxStackTypes, classes, enum variants, exported symbols.
CamelmaxStackFields, properties, parameters, methods in camel-case languages.
Snakemax_stackFiles, modules, fields, functions in snake-case languages.

Language generators choose from these forms and may apply additional language-specific sanitization for invalid identifiers or reserved words.

Qualified schema names are projected as namespaces where the target has a stable module boundary. For example, game.items.Item becomes game/items/item.rs plus Rust modules, Game.Items.Item under the configured C# root namespace, and game/items/item.ts with ESM namespace barrels. Godot uses namespace directories and a globally unique class_name such as GameItemsItem because GDScript classes share a global registry.

Targets whose generated runtime is intentionally a single package or translation unit encode the complete qualified name into a collision-free symbol instead: game.items.Item becomes GameItemsItem or game_items_item according to that language. C, C++, Go, Java, Kotlin, Scala, Dart, Python, Lua, Erlang, and protobuf currently use this representation. This keeps two declarations such as items.Entry and quests.Entry distinct without relying on source-file order.

Language Conventions

The built-in generators follow the target language’s normal public API style:

TargetTypesFields and accessorsFiles/modules
RustPascalCasesnake_casesnake_case.rs
Cprefixed snake_casesnake_casesnake_case.h, snake_case.c
C++PascalCasesnake_casesnake_case.hpp
C#PascalCasePascalCase propertiesPascalCase.cs
GoPascalCase exported namesPascalCase exported fieldssnake_case.go
JavaPascalCaselowerCamelCasePascalCase.java
KotlinPascalCaselowerCamelCasetarget layout
ScalaPascalCaselowerCamelCasePascalCase.scala
TypeScriptPascalCaselowerCamelCasesnake_case.ts
JavaScriptPascalCaselowerCamelCasesnake_case.js, snake_case.d.ts
PythonPascalCasesnake_casesnake_case.py
DartPascalCaselowerCamelCasesnake_case.dart
LuaPascalCase table-like typeslowerCamelCasesnake_case.lua
Erlangsnake_case modulessnake_case map keys/functionssnake_case.erl
GodotPascalCase classessnake_casesnake_case.gd

This table describes generated code identifiers, not runtime data names.

Runtime Names Stay Raw

The following values keep the original schema spelling:

  • canonical qualified table names in bundles and table metadata;
  • field names read from runtime rows;
  • enum string values;
  • union variant tag values;
  • schema lock and fingerprint input.

Changing a schema name changes the data contract. Changing only a target language’s generated identifier style should not.

Custom Type Mappings

Custom type mappings do not rename generated schema identifiers. They only control target-language type expressions, imports/includes, and optional conversion hooks.

Mapping function names are native code written by the user for that target language, so they should follow that language’s own naming convention. The mapping key remains the named schema type, such as Vec3.