feat(ogar-render-askama): build-time codegen harness — mirror of woa-rs#78
Conversation
There was a problem hiding this comment.
💡 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 }}, |
There was a problem hiding this comment.
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 👍 / 👎.
There was a problem hiding this comment.
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).
…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.
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.
5de2eeb to
5ed5880
Compare
What
Build-time askama codegen harness over the calcified canonical layer. Structurally a mirror of
AdaWorldAPI/woa-rscrates/codegen(RFC-v02-006) — same kit shape that's already proven on the WoA → woa-rs route codegen, just withogar_vocab::Classas the typed input instead of a JSON-loadedRouteSpec.RouteSpec(JSON-loaded)ogar_vocab::Class(in-process from class fn)enum HandlerKind(13 variants)enum ArtifactKind(5 today, append-only)trait HandlerKindEmittertrait ArtifactEmitterfor_kind(s) -> Box<dyn …>templates/_dispatch/list_view.htmltemplates/dispatch/rust_struct.askamalist_for_tenant) + 12 stubsRustStruct) + 4 stubsThe 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 Ruststruct+pub const CLASS_ID: u16+ family-edge fields, mapping Rails-side type names to coarse Rust types. Tested againstproject_work_item,billable_work_entry(all 12 family edges surface asVec<u64>/Option<u64>),project_role(typed attributes).TsInterface/SurrealqlTable/OpenapiSchema/NodeGuidRoutingArm—Stubemitter. Compilable, emits a marker comment. The full pipeline (for_kind→emit→ 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.rssource files).ogar-class-view(#77) is run-time (resolves SoA rows throughClassViewtrait 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/A2UIv0.8) carried as T6 follow-on in the README. Same northstar from the output side, deferred.DUSK_Solution/MUIBridgenoted as design lineage, no active deps.Layering