Skip to main content

sim_web_shell/
assets.rs

1//! Embedded WebUI assets.
2//!
3//! The shell and cookbook pages are embedded at compile time so the binary
4//! serves a complete WebUI regardless of the working directory. The current
5//! bundle paints Scene data and emits Intent values; a live runtime session
6//! bridge can replace or extend these assets without changing this routing
7//! contract.
8
9/// A static asset resolved for an HTTP path: its bytes and MIME content type.
10pub struct Asset {
11    /// The response body.
12    pub body: &'static [u8],
13    /// The `Content-Type` header value.
14    pub content_type: &'static str,
15}
16
17const INDEX_HTML: &str = include_str!("../web/index.html");
18const COOKBOOK_HTML: &str = include_str!("../web/cookbook.html");
19const ATELIER_HTML: &str = include_str!("../web/atelier.html");
20const THEME_CSS: &str = include_str!("../web/styles/theme.css");
21const COOKBOOK_CSS: &str = include_str!("../web/cookbook/cookbook.css");
22const ATELIER_CSS: &str = include_str!("../web/atelier/atelier.css");
23const BOOT_JS: &str = include_str!("../web/interpreter/boot.js");
24const APP_JS: &str = include_str!("../web/interpreter/app.js");
25const SCENE_JS: &str = include_str!("../web/interpreter/scene.js");
26const DIFF_JS: &str = include_str!("../web/interpreter/diff.js");
27const INTENT_JS: &str = include_str!("../web/interpreter/intent.js");
28const SESSION_JS: &str = include_str!("../web/interpreter/session.js");
29const COOKBOOK_JS: &str = include_str!("../web/cookbook/cookbook.js");
30const ATELIER_JS: &str = include_str!("../web/atelier/atelier.js");
31
32const JS_CONTENT_TYPE: &str = "text/javascript; charset=utf-8";
33
34/// Resolve a request path to an embedded asset, or `None` for a 404.
35///
36/// `/` and `/index.html` both resolve to the shell page. Only the small set of
37/// embedded assets is routable; everything else is a miss and the server fails
38/// closed with a 404 rather than touching the filesystem.
39pub fn asset_for(path: &str) -> Option<Asset> {
40    let path = path.split(['?', '#']).next().unwrap_or(path);
41    match path {
42        "/" | "/index.html" => Some(Asset {
43            body: INDEX_HTML.as_bytes(),
44            content_type: "text/html; charset=utf-8",
45        }),
46        "/cookbook" | "/cookbook.html" => Some(Asset {
47            body: COOKBOOK_HTML.as_bytes(),
48            content_type: "text/html; charset=utf-8",
49        }),
50        "/atelier" | "/atelier.html" => Some(Asset {
51            body: ATELIER_HTML.as_bytes(),
52            content_type: "text/html; charset=utf-8",
53        }),
54        "/styles/theme.css" => Some(Asset {
55            body: THEME_CSS.as_bytes(),
56            content_type: "text/css; charset=utf-8",
57        }),
58        "/cookbook/cookbook.css" => Some(Asset {
59            body: COOKBOOK_CSS.as_bytes(),
60            content_type: "text/css; charset=utf-8",
61        }),
62        "/atelier/atelier.css" => Some(Asset {
63            body: ATELIER_CSS.as_bytes(),
64            content_type: "text/css; charset=utf-8",
65        }),
66        "/interpreter/boot.js" => Some(Asset {
67            body: BOOT_JS.as_bytes(),
68            content_type: JS_CONTENT_TYPE,
69        }),
70        "/interpreter/app.js" => Some(Asset {
71            body: APP_JS.as_bytes(),
72            content_type: JS_CONTENT_TYPE,
73        }),
74        "/interpreter/scene.js" => Some(Asset {
75            body: SCENE_JS.as_bytes(),
76            content_type: JS_CONTENT_TYPE,
77        }),
78        "/interpreter/diff.js" => Some(Asset {
79            body: DIFF_JS.as_bytes(),
80            content_type: JS_CONTENT_TYPE,
81        }),
82        "/interpreter/intent.js" => Some(Asset {
83            body: INTENT_JS.as_bytes(),
84            content_type: JS_CONTENT_TYPE,
85        }),
86        "/interpreter/session.js" => Some(Asset {
87            body: SESSION_JS.as_bytes(),
88            content_type: JS_CONTENT_TYPE,
89        }),
90        "/cookbook/cookbook.js" => Some(Asset {
91            body: COOKBOOK_JS.as_bytes(),
92            content_type: JS_CONTENT_TYPE,
93        }),
94        "/atelier/atelier.js" => Some(Asset {
95            body: ATELIER_JS.as_bytes(),
96            content_type: JS_CONTENT_TYPE,
97        }),
98        _ => None,
99    }
100}