Skip to content

feat(ogar-render-askama): build-time codegen harness — mirror of woa-rs#78

Merged
AdaWorldAPI merged 2 commits into
mainfrom
claude/ogar-render-askama
Jun 19, 2026
Merged

feat(ogar-render-askama): build-time codegen harness — mirror of woa-rs#78
AdaWorldAPI merged 2 commits into
mainfrom
claude/ogar-render-askama

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

Build-time askama codegen harness over the calcified canonical layer. Structurally a mirror of AdaWorldAPI/woa-rs crates/codegen (RFC-v02-006) — same kit shape that's already proven on the WoA → woa-rs route codegen, just with ogar_vocab::Class as the typed input instead of a JSON-loaded RouteSpec.

WoA-rs (build-time route codegen) OGAR analog (this PR)
RouteSpec (JSON-loaded) ogar_vocab::Class (in-process from class fn)
enum HandlerKind (13 variants) enum ArtifactKind (5 today, append-only)
trait HandlerKindEmitter trait ArtifactEmitter
for_kind(s) -> Box<dyn …> identical
templates/_dispatch/list_view.html templates/dispatch/rust_struct.askama
Phase-0: 1 real emitter (list_for_tenant) + 12 stubs Phase-0: 1 real emitter (RustStruct) + 4 stubs

The 800 → 7-70 collapse

Templates are bounded by artifact kind, not by (class × target). Adding a new canonical concept (one OGAR class fn) costs zero new templates. Adding a new target is one new variant + one askama template; every promoted concept emits through it automatically.

Phase-0 (this PR)

  • ArtifactKind::RustStruct — real emitter + template. Renders a Rust struct + pub const CLASS_ID: u16 + family-edge fields, mapping Rails-side type names to coarse Rust types. Tested against project_work_item, billable_work_entry (all 12 family edges surface as Vec<u64> / Option<u64>), project_role (typed attributes).
  • TsInterface / SurrealqlTable / OpenapiSchema / NodeGuidRoutingArmStub emitter. Compilable, emits a marker comment. The full pipeline (for_kindemit → string) is exercisable end-to-end against every promoted concept before the concrete templates land (T2–T5).

Decoupled from ogar-class-view (PR #77)

This crate is build-time (consumes Class, emits .rs source files). ogar-class-view (#77) is run-time (resolves SoA rows through ClassView trait to render rows for live projection). Both pipelines are askama-templated; both share the N3 field-order convention; they consume different shapes — no overlap.

Tests

cargo test -p ogar-render-askama: 6/6 unit + 1 doctest (ignored — #[derive(Template)] doctest). Workspace check + workspace test green.

A2UI on the roadmap (not this PR)

Per your steer: A2UI integration (AdaWorldAPI/A2UI v0.8) carried as T6 follow-on in the README. Same northstar from the output side, deferred. DUSK_Solution / MUIBridge noted as design lineage, no active deps.

Layering

ogar-vocab           (codebook + Class fns)
      │
      │  pure build-time construction
      ▼
ogar-render-askama   (this PR)
      │
      ▼
.rs / .ts / .surql / .json source text
      │
      ▼
op-codegen-projection / rm-codegen / medcare-codegen / …

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1f7077625e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

pub struct {{ name }} {
{%- for attr in attributes %}
/// `{{ attr.name }}`{% if attr.type_name.len() > 0 %} (curator type: `{{ attr.type_name }}`){% endif %}.
pub {{ attr.snake_name }}: {{ attr.rust_type }},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Escape Rust keywords in generated field names

In the current promoted codebook, project_actor() defines an attribute named type (crates/ogar-vocab/src/lib.rs:2245), so rendering that class as RustStruct emits pub type: String, and the generated Rust cannot compile. Since this crate advertises rendering every promoted concept, field names need to be normalized or raw-escaped (for example r#type) before reaching the template.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed (commit 5de2eeb -> rebased into 5ed5880). escape_rust_ident() wraps a conservative Rust-2024-strict+reserved-future list and the template renders escaped names. Regression test rust_struct_escapes_keyword_attribute_names pins project_actor's type -> pub r#type: (and the illegal form must NOT appear).

AdaWorldAPI pushed a commit that referenced this pull request Jun 19, 2026
…ex P1 on #78)

project_actor() ships an attribute named `type` (Rails STI convention),
which the naive template emitted as `pub type: String,` — illegal Rust.

Conservative escape list (Rust 2024 strict + reserved-future) applied in
escape_rust_ident(); curator slots like `type` / `match` / `move` / etc.
now render as `pub r#type:` / `pub r#match:` / `pub r#move:` and compile.

Regression test pinned: project_actor's `type` attribute must emit
`pub r#type:` and must NOT emit `pub type:`.

7/7 unit tests green.
claude added 2 commits June 19, 2026 21:13
Adds the build-time askama codegen harness over the calcified canonical
layer. Structurally a mirror of AdaWorldAPI/woa-rs crates/codegen
(RFC-v02-006): the same shape WoA-rs uses for its route codegen, but the
typed input is ogar_vocab::Class instead of a JSON-loaded RouteSpec.

The kit:
- enum ArtifactKind { RustStruct, TsInterface, SurrealqlTable,
  OpenapiSchema, NodeGuidRoutingArm } — append-only.
- trait ArtifactEmitter — one fn `emit(spec) -> Result<String, askama::Error>`.
- artifact_kinds::for_kind(kind) -> Box<dyn ArtifactEmitter> — dispatcher.
- templates/dispatch/<kind>.askama — one template per kind, no class name
  ever hardcoded.

Phase-0 (this PR):
- ArtifactKind::RustStruct: REAL emitter + template. Renders a Rust
  struct + `pub const CLASS_ID: u16` + family-edge fields from the
  canonical class, mapping Rails-side type names to coarse Rust types
  (downstream consumers specialise per their precision needs).
- The other 4 kinds: Stub emitter — compilable, emits a marker comment,
  exercises the full pipeline (lookup + dispatch + return) before the
  concrete templates land.

The 800 -> 7-70 collapse: adding a new canonical concept costs ZERO new
templates (flows through the existing kit). Adding a new target is one
ArtifactKind variant + one askama template; every promoted concept emits
through it automatically.

Decoupled from ogar-class-view (PR #77):
- ogar-render-askama is BUILD-TIME (consumes Class, emits source files).
- ogar-class-view is RUN-TIME (resolves SoA rows through ClassView trait
  to render rows for live projection).
Both pipelines are askama-templated; both share the N3 field order
convention; they consume different shapes — they don't compete.

A2UI integration (AdaWorldAPI/A2UI v0.8) carried as T6 on the roadmap
only — same northstar from the output side, deferred. DUSK_Solution /
MUIBridge noted as design lineage, no active deps.

Tests: 6/6 unit + 1 doctest (ignored — askama derive). Workspace check
and workspace test green.
…ex P1 on #78)

project_actor() ships an attribute named `type` (Rails STI convention),
which the naive template emitted as `pub type: String,` — illegal Rust.

Conservative escape list (Rust 2024 strict + reserved-future) applied in
escape_rust_ident(); curator slots like `type` / `match` / `move` / etc.
now render as `pub r#type:` / `pub r#match:` / `pub r#move:` and compile.

Regression test pinned: project_actor's `type` attribute must emit
`pub r#type:` and must NOT emit `pub type:`.

7/7 unit tests green.
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.

2 participants