rac_engine/portal.rs
1//! Portal HTML assembly (`decided.output.portal`) — inject the export payload
2//! into the vendored shell, per PORT-CONTRACT.d/17 §2.
3//!
4//! The shell is the exact packaged asset the oracle ships
5//! (`src/asdecided/templates/portal/asdecided-portal-shell.html`, vendored from
6//! lore-web @ ed4dd42, 182669 bytes), embedded at compile time so the
7//! emitted HTML is byte-identical. The unit test below pins the embed to
8//! the Python package file: re-vendor `assets/portal/` in lockstep
9//! whenever the oracle's shell changes.
10
11use crate::export::CorpusExport;
12
13/// The packaged Portal shell, embedded verbatim.
14pub const SHELL: &str = include_str!("../assets/portal/asdecided-portal-shell.html");
15
16/// The exact empty data seam the shell-only viewer build emits (no
17/// whitespace inside the element); the populated form replaces it verbatim.
18const SEAM: &str = r#"<script type="application/json" id="lore-export"></script>"#;
19
20/// `_escape_for_script(payload)` — make serialized JSON safe inside a
21/// `<script>` element with two valid JSON escapes, applied in the oracle's
22/// order: `</` → `<\/` first, then `<!--` → `<\u{0021}--` (both literal
23/// `str.replace` passes over the whole serialized document).
24fn escape_for_script(payload: &str) -> String {
25 payload.replace("</", "<\\/").replace("<!--", "<\\u0021--")
26}
27
28/// `render_export_html(export)` — the vendored shell with the export JSON
29/// injected into its single data seam.
30///
31/// `PortalShellMissing` is unreachable with a compile-time embed; the
32/// `PortalSeamMissing` guard is retained for contract fidelity (its message
33/// bytes feed `decided: <msg>`, exit 2). Both are operational errors in the
34/// oracle, not caller errors.
35pub fn render_export_html(export: &CorpusExport) -> Result<String, String> {
36 if SHELL.matches(SEAM).count() != 1 {
37 return Err(format!(
38 "packaged portal shell has no usable data seam \
39 ({SEAM}); re-vendor it: cd decided-localview && npm run vendor:shell"
40 ));
41 }
42 let payload = escape_for_script(&crate::output::render_export_json(export));
43 let populated =
44 format!(r#"<script type="application/json" id="lore-export">{payload}</script>"#);
45 // `str.replace` on a count-1 needle: a single substitution.
46 Ok(SHELL.replacen(SEAM, &populated, 1))
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52
53 #[test]
54 fn shell_carries_exactly_one_empty_seam() {
55 assert_eq!(SHELL.matches(SEAM).count(), 1);
56 }
57
58 /// The two escapes in the oracle's order; the comment-open rewrite must
59 /// not be disturbed by the `</` pass.
60 #[test]
61 fn escape_order_and_exactness() {
62 assert_eq!(
63 escape_for_script(r#"a</script>b<!--c"#),
64 r#"a<\/script>b<\u0021--c"#
65 );
66 assert_eq!(escape_for_script("plain"), "plain");
67 }
68}