Skip to content

Add package-level datasource declarations#361

Merged
corepunch merged 19 commits into
mainfrom
issue-360-datasource-package-declarations
Jul 5, 2026
Merged

Add package-level datasource declarations#361
corepunch merged 19 commits into
mainfrom
issue-360-datasource-package-declarations

Conversation

@corepunch

@corepunch corepunch commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

This PR implements the datasource provider gateway pattern — package.lua declares named data sources, views bind to them by name, and providers handle fetch/save/revert behind a message-driven contract.

Architecture

package.lua                  registry                   view XML
---                           ---                        ---
DataSources = {       FS_RegisterDataSource()   DataContextSource="ApplicationData"
  { Name="AppData",                              ItemsSource="ApplicationData:Metrics"
    Type="Xml",                      
    Params="Path=...",            FS_ResolveDataSource("AppData")
    Schema="..." }                             
                                        _xml_ds_fetch(params)
                                                  |
                                           FS_LoadObject(path)
                                                  |
                                          DataObject tree
                                                  |
                                          DataContext / ItemsSource

How it's done

Provider registry (filesystem.c):

  • Static arrays for provider types (type_name + fetch/save/revert callbacks), datasource entries (name, type, params, owning project, generation), and runtime sessions (cached root, dirty flag, optional schema).
  • FS_RegisterDataProvider accepts fetch + save + revert callbacks.
  • FS_RegisterProjectDataSource stores entries with project ownership.
  • FS_ResolveDataSource checks session cache first, then walks entries → finds matching provider type → calls fetch → caches in session.
  • Project-scoped cleanup via FS_ClearProjectDataSources (removes entries + invalidates orphaned sessions). Full reset via FS_ClearAllDataSources.

Lifecycle (w_filesystem.c):

  • filesystem.initFS_LoadBundle → registers each DataSource via FS_RegisterProjectDataSource, passing the optional Schema field appended to params as &Schema=....
  • filesystem.unloadProjectFS_ClearProjectDataSources + OBJ_Clear + OBJ_RemoveFromParent.
  • Project.Object.Release handler fires on object destruction as a safety net.

Master-detail (UIKit.cgen, ListBox.c, object_hierarchy.c):

  • ItemsControl.SelectedItem (DataObject*) added to the .cgen, auto-generated as a struct field with property ID/short-ID registration.
  • ListBox sets SelectedItem = itemNode->DataContext in ListBox_SetSelected (and ListBox_SyncToSelectedValue for binding-driven changes).
  • OBJ_FindPropertyByPath extended: when a path segment isn't a child object, it tries finding an object-typed property, reads the component pointer, calls CMP_GetObject, and continues resolution on the pointed-to object. This enables paths like SelectedItem/Title.

Data mutations (filesystem.c, fs_local.h):

  • ds_provider struct extended with save and revert function pointers.
  • FS_SaveDataSource(name): finds session → calls provider's save(root, params) → marks clean on success.
  • FS_RevertDataSource(name): calls provider's revert(root, params) → marks clean.
  • FS_MarkDataSourceDirty(name): sets session dirty flag for tracked changes.
  • XML provider's _xml_ds_save: serializes DataObject tree to XML via FS_SerializeObjectToXmlString and writes to the file path from params.
  • XML provider's _xml_ds_revert: invalidates session cache (next ResolveDataSource reloads).
  • Screen.SaveChanges message registered as a hook point for UI-level save triggers.

Deprecation + sample migration (fs_xml.c, property_lua.c):

  • DataContextSource handler: after provider lookup fails, emits Con_Warning before file-path fallback.
  • property_lua.c kDataTypeObject handler: tries FS_ResolveDataSource before FS_LoadObject, with deprecation warning on fallback.
  • All Example and Adventure XML files updated: ItemsSource="ApplicationData:Metrics" instead of Example/Data/ApplicationData:Metrics.

Optional schema (filesystem.c, filesystem.cgen):

  • DataSource struct gains a Schema (string) field.
  • ds_schema / ds_entity / ds_column structs hold parsed column metadata.
  • _ParseSchema uses libxml2 to read <Schema><Entity><Column> hierarchy.
  • Schema is parsed once on first resolve and cached in the session.
  • FS_GetDataSourceSchema and FS_FindSchemaColumn provide runtime metadata access.
  • Freed on session/registry cleanup.

Test strategy

Test file Tests
test_layout.lua DataSource declarations, provider resolution, name:child syntax, lifecycle cleanup, repeated load/unload, schema loading
test_pagehost_listbox.lua SelectedItem default, programmatic, SelectItem message, click, custom ValueProperty, auto-select, reference stability, path resolution, GridBox inheritance

Files changed

File Change
source/filesystem/filesystem.cgen DataSource sealed struct (+ Schema field), Project.DataSources array
source/filesystem/filesystem.c Provider registry, sessions, lifecycle, save/revert, schema parsing, XML save/revert
source/filesystem/fs_local.h All internal API declarations
source/filesystem/fs_xml.c Deprecation warning, name-based DataContextSource resolution
source/filesystem/w_filesystem.c Provider registration with save/revert, unloadProject Lua API, Project.Release handler
source/core/property/property_lua.c Provider lookup before file path for object properties
source/core/object/object_hierarchy.c OBJ_FindPropertyByPath navigates through object properties
plugins/UIKit/UIKit.cgen ItemsControl.SelectedItem, Screen.SaveChanges message
plugins/UIKit/ListBox.c Set SelectedItem on selection change
plugins/UIKit/Screen.c SaveChanges handler
samples/Example/package.lua DataSources with Schema reference
samples/Example/Screens/Application.xml Name-based ItemsSource/DataContextSource
samples/Example/Data/ApplicationData.Schema.xml Sample schema
samples/Adventure/Screens/Adventures.xml Name-based ItemsSource
samples/Adventure/Pages/GameList.xml Name-based ItemsSource
tests/test_layout.lua Lifecycle, schema, and provider tests
tests/test_pagehost_listbox.lua SelectedItem tests

Validation

  • make test-headless — all tests pass, zero regressions

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces package-level datasource declarations so package.lua can declare datasource provider metadata (name/type/params) and have it surface on Project.DataSources, alongside updates to sample packages and a regression test. It also updates Lua↔C property hydration to better handle struct/object tables used by package metadata.

Changes:

  • Add Project.DataSources as a new schema surface (with a sealed DataSource struct) in the filesystem module.
  • Declare XML datasources in samples/Example/package.lua and samples/Adventure/package.lua.
  • Extend Lua property hydration/serialization to handle struct/object tables and array properties; add a focused layout test for package datasource declarations.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/test_layout.lua Adds a regression test asserting package-level datasource declarations are present for the Example sample.
source/filesystem/filesystem.cgen Adds the DataSource struct and the Project.DataSources array property to the filesystem schema.
source/core/property/property_lua.c Updates Lua property read/write logic to better handle struct/object tables and array properties.
samples/Example/package.lua Adds an Example datasource declaration (ApplicationData).
samples/Adventure/package.lua Adds Adventure datasource declarations (Games, Tabs).
Comments suppressed due to low confidence (1)

source/core/property/property_lua.c:105

  • Array property deserialization iterates the Lua table with lua_next but writes elements sequentially via an incrementing index. If the table is sparse (e.g. { [3] = ... }) or contains numeric keys outside 1..lua_rawlen, this will write past the calloc() buffer (or into a 0-length allocation), causing memory corruption/crashes. Use the numeric key to compute the destination index and validate it is within 1..numitems before writing.
    while (lua_next(L, idx) != 0) {
      if (lua_type(L, -2) != LUA_TNUMBER) {
        free(tmp);
        luaL_error(L, "Expected numeric keys in array table for property %s", prop->Name);
      }

Comment thread source/core/property/property_lua.c Outdated
Comment on lines +274 to +276
if (prop->IsArray) {
void *items = valueptr ? *(void *const*)valueptr : NULL;
int count = valueptr ? ((int const*)valueptr)[sizeof(void*)/sizeof(int)] : 0;
corepunch added 2 commits July 3, 2026 16:02
- Add provider registry: FS_RegisterDataSourceProvider, FS_RegisterDataSource, FS_ResolveDataSource
- Register XML provider as first concrete fetch handler
- Rewrite DataContextSource to resolve names through registry with file-path fallback
- Register package DataSources into global registry at bundle load
- Add tests for provider-based DataContextSource resolution

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

source/core/property/property_lua.c:105

  • Array-property decoding allocates storage based on lua_rawlen() but iterates elements via lua_next() without enforcing contiguous 1-based keys. Sparse arrays (e.g. { [1]=..., [3]=... }) will overflow the allocated buffer as i increments past numitems. Add a key check to require contiguous 1..N numeric indices (or switch to a lua_rawgeti loop).
    while (lua_next(L, idx) != 0) {
      if (lua_type(L, -2) != LUA_TNUMBER) {
        free(tmp);
        luaL_error(L, "Expected numeric keys in array table for property %s", prop->Name);
      }

Comment thread source/filesystem/fs_xml.c Outdated
Comment on lines +55 to +59
struct Object *dataObj = FS_ResolveDataSource(ds_name, NULL);
if (!dataObj) {
// Fallback: treat as a direct file path (backward compatibility)
dataObj = FS_LoadObject(value);
}
Comment on lines +49 to +60
void
FS_RegisterDataSource(const char *name, const char *type, const char *params)
{
if (_ds_entry_count >= MAX_DATASOURCE_ENTRIES) {
Con_Error("No space to register datasource '%s'", name);
return;
}
struct ds_entry *e = &_ds_entries[_ds_entry_count++];
strncpy(e->name, name, sizeof(e->name) - 1);
strncpy(e->type, type, sizeof(e->type) - 1);
strncpy(e->params, params, sizeof(e->params) - 1);
}
- Add SelectedItem (DataObject*) property to ItemsControl in UIKit.cgen
- ListBox sets SelectedItem to clicked item's DataContext on selection
- OBJ_FindPropertyByPath now navigates through object-typed properties
  in multi-segment paths (e.g. SelectedItem/Title)
- 9 tests covering all selection paths and master-detail data navigation
@corepunch

Copy link
Copy Markdown
Owner Author

@copilot Fix webgl build, why is it broken?

Co-authored-by: corepunch <83646194+corepunch@users.noreply.github.com>

Copilot AI commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

@copilot Fix webgl build, why is it broken?

Fixed in f007338. The WebGL job was failing because plugins/UIKit/ListBox.c calls PROP_NotifyChanged without including its declaration; the Emscripten/clang build treats that as an error, while the native build didn’t surface it.

corepunch added 5 commits July 4, 2026 12:27
- Add FS_RegisterDataProvider with save/revert callbacks
- Add FS_ClearProjectDataSources for per-project cleanup
- Add session cache for FS_ResolveDataSource
- Add FS_MarkDataSourceDirty, FS_SaveDataSource, FS_RevertDataSource
- Add FS_ClearAllDataSources to FS_Shutdown
- Add unloadProject Lua API for explicit project unload
- Add Screen.SaveChanges message handler
- Update XML provider with save/revert implementations
- Add lifecycle tests: cleanup, reload, repeated load/unload
- Update existing datasource tests to use unloadProject
…warnings

- Update Example/Application.xml: DataContextSource and ItemsSource use
  datasource names instead of direct file paths (e.g. 'ApplicationData:Metrics')
- Update Adventure/Adventures.xml: ItemsSource='Tabs' instead of 'Adventure/Data/Tabs'
- Update Adventure/GameList.xml: ItemsSource='Games:Catalog' instead of 'Adventure/Data/Games:Catalog'
- Add provider registry lookup to property_lua.c kDataTypeObject handler
- Add Con_Warning when file-path fallback is taken in fs_xml.c and property_lua.c
- Update test_layout.lua to match new name-based syntax
- Add Schema field to DataSource struct in filesystem.cgen
- Add ds_schema, ds_entity, ds_column structures for column metadata
- Add _ParseSchema to load schema XML files (libxml2-based)
- Schema is cached in ds_session on first resolve
- FS_GetDataSourceSchema and FS_FindSchemaColumn for querying metadata
- Schema path is appended to params as &Schema= for provider transport
- Example project: add ApplicationData.Schema.xml with Signals/Metrics entities
- Example package.lua: reference schema in DataSource declaration
- Memory cleanup: free schemas on session/registry teardown
- Tests: schema file load, schema field in package.lua, schema in params
…taSourceLibrary

- Replace flat DataSources struct array with DataSourceLibrary using
  XmlDataSource objects in package.lua samples
- Add Object.Start handler for XmlDataSource to load XML source
- Fall back to registered class constructors in OBJ_GetProperty for
  declarative environments
- Treat array-part userdata entries as children in create_object_from_table
- Update FS_LoadBundle to iterate DataSourceLibrary children instead of
  DataSources array
Resolve datasource classes explicitly with
require 'orca.filesystem.XmlDataSource' instead of a global class-name
fallback in OBJ_GetProperty.

- revert the OBJ_GetProperty class-registry fallback
- give package.lua a proxy _ENV: reads fall back to _G (so require works),
  writes forward to the project object
- add an orca.<module>.<Class> package.searchers entry (fs_findclass)
- port Example and Adventure samples to require the class
@corepunch

Copy link
Copy Markdown
Owner Author

@copilot See if still makes sense to address the review comments, if it is, provide fixes

Co-authored-by: corepunch <83646194+corepunch@users.noreply.github.com>

Copilot AI commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

@copilot See if still makes sense to address the review comments, if it is, provide fixes

Still-valid review comments were worth fixing, and I addressed them in 880fd39: guarded NULL-backed array serialization, fixed the DataContextSource="path:child" fallback to load the stripped path, and de-duplicated datasource registration with safe string termination. I also added a layout regression for the direct-path child fallback.

corepunch added 2 commits July 4, 2026 23:40
…inline

Remove attach_children flag and separate attach_object_children_from_table
function — child attachment is now unconditional in the single table pass.
corepunch added 5 commits July 5, 2026 09:12
- add XML aliases: group/object, string/int/float/bool
- accept lowercase attribute aliases in XML loader
- map text node content to Value when value attr is omitted
- migrate Adventure datasource XML to compact style
- keep datasource registration deterministic at project load
- XmlDataSource.Start eagerly loads XML into owned DataObject tree
- FS_ResolveDataSource checks for owned DataObject before session fallback
- ListBox.Start refactored to compact C idioms
- Added Data property to DataSource class
- Documented compact C coding style in AGENTS.md
- Introduce DataContext as base for both DataSource and DataObject
- Move Data property from DataSource up to DataContext
- Add core_GetDataContextData helper to resolve root DataObject
- Update binding engine to go through DataContext chain
- Set XmlDataSource (not DataObject) as Node.DataContext
- Add FS_FindDataSource for non-resolving lookups
- Update DataContextSource attr to retain DataSource reference
- Replace DataContext.Data property with DataContext.GetData message
- Remove core_GetDataContextData helper; use _SendMessage instead
- DataSource stores its DataObject in a field, handles GetData
- DataObject handles GetData via OBJ_FindByPath through children
- Binding engine splits path at '/' and resolves via GetData
- ListBox: pass pointer arg to PROP_SetValue for string property
- Add tests for default template, data wiring, nested paths
@corepunch

Copy link
Copy Markdown
Owner Author

@copilot Webgl fails, see what's wrong and fix

Co-authored-by: corepunch <83646194+corepunch@users.noreply.github.com>

Copilot AI commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

@copilot Webgl fails, see what's wrong and fix

Fixed in bd2d37c. The WebGL job was failing because Con_Printf expands with a trailing semicolon under __EMSCRIPTEN__, so the comma-return form in source/filesystem/w_filesystem.c produced invalid syntax; I replaced that pattern there and in the same plugins/UIKit/ListBox.c handler with explicit Con_Printf(...) + return FALSE; blocks.

@corepunch corepunch marked this pull request as ready for review July 5, 2026 12:59
@corepunch corepunch merged commit 8f168f1 into main Jul 5, 2026
4 checks passed
@corepunch corepunch deleted the issue-360-datasource-package-declarations branch July 5, 2026 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants