Skip to main content

mini_static/
reload.rs

1use std::path::Path;
2
3use bytes::Bytes;
4use serde_json::json;
5
6/// The type of change detected in a watched file.
7///
8/// Used by live-reload to determine what the browser should do when a file changes:
9/// CSS stylesheets are hot-swapped, while scripts and HTML require a full page reload.
10#[derive(Clone, Debug, PartialEq, Eq)]
11pub enum ChangeType {
12	/// A CSS stylesheet changed.
13	Css,
14	/// A JavaScript module changed.
15	Script,
16	/// An HTML page changed.
17	Html,
18	/// Some other file changed.
19	Other,
20}
21
22impl ChangeType {
23	/// Determine the change type from a file path's extension.
24	pub fn from_path(path: &Path) -> Self {
25		match path.extension().and_then(|e| e.to_str()) {
26			Some("css") => ChangeType::Css,
27			Some("js" | "mjs") => ChangeType::Script,
28			Some("html" | "htm") => ChangeType::Html,
29			_ => ChangeType::Other,
30		}
31	}
32
33	/// The string representation used in SSE event names and JSON.
34	pub fn as_str(&self) -> &'static str {
35		match self {
36			ChangeType::Css => "css",
37			ChangeType::Script => "script",
38			ChangeType::Html => "html",
39			ChangeType::Other => "other",
40		}
41	}
42}
43
44/// Encode a reload event as an SSE (Server-Sent Events) frame.
45///
46/// The frame follows the SSE text/event-stream format:
47/// ```text
48/// event: <change_type>
49/// data: {"type": "<change_type>"}
50///
51/// ```
52///
53/// This is the single canonical place the SSE frame format is defined.
54/// Callers that forward reload events over HTTP (e.g., mini-unified) call this
55/// function rather than re-implementing the byte-for-byte format.
56pub fn reload_event_frame(change_type: &ChangeType) -> Bytes {
57	let data = json!({ "type": change_type.as_str() });
58	let msg = format!(
59		"event: {}\ndata: {}\n\n",
60		change_type.as_str(),
61		data
62	);
63	Bytes::from(msg)
64}
65
66#[cfg(test)]
67mod tests {
68	use super::*;
69
70	#[test]
71	fn reload_event_frame_formats_css_correctly() {
72		let frame = reload_event_frame(&ChangeType::Css);
73		let s = String::from_utf8(frame.to_vec()).unwrap();
74		assert_eq!(s, "event: css\ndata: {\"type\":\"css\"}\n\n");
75	}
76
77	#[test]
78	fn reload_event_frame_formats_script_correctly() {
79		let frame = reload_event_frame(&ChangeType::Script);
80		let s = String::from_utf8(frame.to_vec()).unwrap();
81		assert_eq!(s, "event: script\ndata: {\"type\":\"script\"}\n\n");
82	}
83
84	#[test]
85	fn reload_event_frame_formats_html_correctly() {
86		let frame = reload_event_frame(&ChangeType::Html);
87		let s = String::from_utf8(frame.to_vec()).unwrap();
88		assert_eq!(s, "event: html\ndata: {\"type\":\"html\"}\n\n");
89	}
90
91	#[test]
92	fn reload_event_frame_formats_other_correctly() {
93		let frame = reload_event_frame(&ChangeType::Other);
94		let s = String::from_utf8(frame.to_vec()).unwrap();
95		assert_eq!(s, "event: other\ndata: {\"type\":\"other\"}\n\n");
96	}
97
98	#[test]
99	fn change_type_from_path_css() {
100		assert_eq!(ChangeType::from_path(Path::new("style.css")), ChangeType::Css);
101		assert_eq!(ChangeType::from_path(Path::new("dir/main.css")), ChangeType::Css);
102	}
103
104	#[test]
105	fn change_type_from_path_script() {
106		assert_eq!(ChangeType::from_path(Path::new("app.js")), ChangeType::Script);
107		assert_eq!(ChangeType::from_path(Path::new("mod.mjs")), ChangeType::Script);
108		assert_eq!(ChangeType::from_path(Path::new("dir/lib.js")), ChangeType::Script);
109	}
110
111	#[test]
112	fn change_type_from_path_html() {
113		assert_eq!(ChangeType::from_path(Path::new("index.html")), ChangeType::Html);
114		assert_eq!(ChangeType::from_path(Path::new("page.htm")), ChangeType::Html);
115		assert_eq!(ChangeType::from_path(Path::new("dir/file.html")), ChangeType::Html);
116	}
117
118	#[test]
119	fn change_type_from_path_other() {
120		assert_eq!(ChangeType::from_path(Path::new("image.png")), ChangeType::Other);
121		assert_eq!(ChangeType::from_path(Path::new("data.json")), ChangeType::Other);
122		assert_eq!(ChangeType::from_path(Path::new("README")), ChangeType::Other);
123	}
124}