Skip to main content

max / goingson

6.9 KB · 154 lines History Blame Raw
1 //! The files a described document asks for that are not descriptions.
2 //!
3 //! The window loads from the `quasi` scheme, so the protocol handler sees every
4 //! request the document makes: five stylesheets, three scripts and three fonts.
5 //! Four of the stylesheets are embedded and one, the theme, is rendered at
6 //! startup from the stored choice.
7 //! None of them is a route, and [`quasi_tauri::Protocol::passthrough`] is where
8 //! they belong.
9 //!
10 //! Embedded rather than read off disk. A Tauri bundle's frontend is inside the
11 //! binary, so there is no directory to read from in a shipped app, and an app
12 //! that served its own stylesheet from a path the installer chose would work in
13 //! `cargo run` and break everywhere else.
14 //!
15 //! The address space here and the router's are kept apart on purpose: a
16 //! passthrough runs first and wins, so a route under `/static` or `/fonts`
17 //! would be shadowed rather than merged.
18
19 use quasi_tauri::Served;
20
21 /// The bytes at a path, if this is one of ours.
22 ///
23 /// Takes the path alone. A stylesheet is a static thing at an address, so the
24 /// verb is not part of the question.
25 pub fn get(path: &str) -> Option<Served> {
26 // The one address here whose bytes are not in the binary: the theme is a
27 // config value resolved at startup, so it is rendered rather than embedded.
28 // See [`super::theming`].
29 if path == super::theming::ADDRESS {
30 return Some(Served::new(CSS, super::theming::css().as_bytes()));
31 }
32
33 let (content_type, body): (&str, &[u8]) = match path {
34 // The cascade, in the order index.html links it: geometry and
35 // typography before the sheet that reads their custom properties.
36 "/static/typography.css" => (CSS, include_bytes!("../../frontend/css/typography.css")),
37 "/static/geometry.css" => (CSS, include_bytes!("../../frontend/css/geometry.css")),
38 "/static/timing.css" => (CSS, include_bytes!("../../frontend/css/timing.css")),
39 "/static/layout.css" => (CSS, include_bytes!("../../frontend/css/layout.css")),
40 "/static/styles.css" => (CSS, include_bytes!("../../frontend/css/styles.css")),
41
42 // htmx is vendored under `frontend/vendor/`, byte for byte the copy
43 // the MNW server serves, so the two hosts running the same
44 // descriptions run the same runtime. Out of `js/` because the
45 // design-system lint polices that directory as our own code and a
46 // minified library calls `confirm()` inside itself.
47 // The rest are written out by `build.rs` from the renderer crate's
48 // own consts, so they cannot fossilise against the markup they read.
49 // Every one the shell links has to be here: an address the shell names
50 // and this match does not is a 404 per document.
51 "/static/htmx.min.js" => (JS, include_bytes!("../../frontend/vendor/htmx.min.js")),
52 "/static/quasi-selection.js" => {
53 (JS, include_bytes!("../../frontend/js/quasi-selection.js"))
54 }
55 "/static/quasi-clock.js" => (JS, include_bytes!("../../frontend/js/quasi-clock.js")),
56
57 // The host half of `Action::by_host`: picking a file is not describable
58 // and never was. Hand-written, and the only such file left after the
59 // swap. See `frontend/js/host.js` for why it is allowed to exist.
60 "/static/host.js" => (JS, include_bytes!("../../frontend/js/host.js")),
61 "/static/quasi-download.js" => (JS, include_bytes!("../../frontend/js/quasi-download.js")),
62 "/static/quasi-fill.js" => (JS, include_bytes!("../../frontend/js/quasi-fill.js")),
63 "/static/quasi-reveal.js" => (JS, include_bytes!("../../frontend/js/quasi-reveal.js")),
64 "/static/quasi-repeat.js" => (JS, include_bytes!("../../frontend/js/quasi-repeat.js")),
65
66 // `/static/../fonts/X` off typography.css, which the URL parser has
67 // already resolved by the time it reaches here.
68 "/fonts/QuasiMono.woff2" => (
69 WOFF2,
70 include_bytes!("../../frontend/fonts/QuasiMono.woff2"),
71 ),
72 "/fonts/QuasiBody.woff2" => (
73 WOFF2,
74 include_bytes!("../../frontend/fonts/QuasiBody.woff2"),
75 ),
76 "/fonts/Reglo-Bold.woff2" => (
77 WOFF2,
78 include_bytes!("../../frontend/fonts/Reglo-Bold.woff2"),
79 ),
80
81 _ => return None,
82 };
83 Some(Served::new(content_type, body))
84 }
85
86 const CSS: &str = "text/css; charset=utf-8";
87 const JS: &str = "text/javascript; charset=utf-8";
88 const WOFF2: &str = "font/woff2";
89
90 #[cfg(test)]
91 mod tests {
92 use super::get;
93
94 #[test]
95 fn every_asset_the_shell_names_is_served() {
96 // The shell in `super::super::protocol` names these. A miss here is a
97 // 404 in a running window, which reads as an unstyled screen rather
98 // than as a missing file.
99 for path in [
100 "/static/typography.css",
101 "/static/geometry.css",
102 "/static/timing.css",
103 "/static/layout.css",
104 "/static/styles.css",
105 "/static/htmx.min.js",
106 "/static/quasi-selection.js",
107 "/static/quasi-clock.js",
108 "/static/quasi-download.js",
109 "/static/quasi-fill.js",
110 "/static/quasi-reveal.js",
111 "/static/quasi-repeat.js",
112 "/fonts/QuasiMono.woff2",
113 "/fonts/QuasiBody.woff2",
114 "/fonts/Reglo-Bold.woff2",
115 ] {
116 let served = get(path).unwrap_or_else(|| panic!("{path} is not served"));
117 assert!(!served.body.is_empty(), "{path} is empty");
118 }
119 }
120
121 /// The theme is the one address whose body is not in the binary, so it is
122 /// held apart from the list above rather than added to it.
123 ///
124 /// Empty here, and that is the answer rather than a gap in the test. The
125 /// sheet is rendered by `theming::install` from the stored choice, and no
126 /// test process has an `AppState` to install from. A document served in
127 /// that window renders on the stock intent tokens `css/styles.css`
128 /// declares, which is what those are for.
129 ///
130 /// What this holds is the half that would otherwise fail silently: the
131 /// address answers at all. A 404 here is an unstyled window, and the
132 /// address is named in three places.
133 #[test]
134 fn the_theme_is_served_even_before_it_is_installed() {
135 let path = crate::quasi::theming::ADDRESS;
136 let served = get(path).unwrap_or_else(|| panic!("{path} is not served"));
137 assert_eq!(served.content_type, super::CSS);
138 assert_eq!(
139 served.body,
140 crate::quasi::theming::css().as_bytes(),
141 "the body is whatever theming holds, installed or not"
142 );
143 }
144
145 #[test]
146 fn a_route_address_is_not_an_asset_address() {
147 // The passthrough runs before the router and wins, so anything the
148 // router owns must miss here.
149 for path in ["/", "/projects", "/tasks", "/static", "/static/"] {
150 assert!(get(path).is_none(), "{path} must reach the router");
151 }
152 }
153 }
154