rustlavel_chart/
plugin.rs1use rustlavel_core::Config;
4use rustlavel_http::plugin::{Plugin, Setup};
5use rustlavel_http::{Request, Response};
6
7pub const CHART_JS: &str = include_str!("../assets/chart.umd.js");
11
12pub const INIT_JS: &str = include_str!("../assets/rustlavel-chart.js");
14
15pub const CHART_JS_PATH: &str = "/vendor/chart/chart.umd.js";
16pub const INIT_JS_PATH: &str = "/vendor/chart/rustlavel-chart.js";
17
18pub const CHART_JS_VERSION: &str = "4.5.1";
21
22pub struct Charts {
38 prefix: String,
39}
40
41impl Default for Charts {
42 fn default() -> Charts {
43 Charts { prefix: "/vendor/chart".to_string() }
44 }
45}
46
47impl Charts {
48 pub fn at(prefix: impl Into<String>) -> Charts {
53 Charts { prefix: prefix.into().trim_end_matches('/').to_string() }
54 }
55}
56
57impl Plugin for Charts {
58 fn name(&self) -> &'static str {
59 "charts"
60 }
61
62 fn register(self: Box<Self>, setup: &mut Setup<'_>) {
63 let immutable = immutable_caching(setup.config);
64
65 setup.router.get(&format!("{}/chart.umd.js", self.prefix), move |_: Request| async move {
66 script(CHART_JS, immutable)
67 });
68 setup.router.get(&format!("{}/rustlavel-chart.js", self.prefix), move |_: Request| async move {
69 script(INIT_JS, immutable)
70 });
71 }
72}
73
74fn immutable_caching(config: &Config) -> bool {
78 config.is_production()
79}
80
81fn script(body: &'static str, immutable: bool) -> Response {
82 let response = Response::ok()
83 .with_body(body)
84 .with_header("content-type", "text/javascript; charset=utf-8");
85 if immutable {
86 return response.with_header("cache-control", "public, max-age=31536000, immutable");
87 }
88 response.with_header("cache-control", "no-cache")
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn the_vendored_library_is_the_version_the_constant_claims() {
97 let banner: String = CHART_JS.lines().take(6).collect::<Vec<_>>().join("\n");
98 assert!(
99 banner.contains(&format!("Chart.js v{CHART_JS_VERSION}")),
100 "the vendored file is not v{CHART_JS_VERSION}: {banner}"
101 );
102 assert!(banner.contains("MIT"), "the copyright banner was stripped: {banner}");
103 }
104
105 #[test]
107 fn the_licence_travels_with_the_library() {
108 let licence = include_str!("../assets/LICENSE-chart.js.md");
109 assert!(licence.contains("MIT License"), "{licence}");
110 assert!(licence.contains("Chart.js Contributors"));
111 }
112
113 #[test]
115 fn nothing_is_fetched_from_a_cdn() {
116 for script in [INIT_JS, CHART_JS] {
117 for host in ["cdn.jsdelivr.net", "unpkg.com", "cdnjs.cloudflare.com"] {
118 assert!(!script.contains(host), "a vendored script still reaches {host}");
119 }
120 }
121 }
122
123 #[test]
126 fn the_default_mount_matches_the_published_paths() {
127 let charts = Charts::default();
128 assert_eq!(format!("{}/chart.umd.js", charts.prefix), CHART_JS_PATH);
129 assert_eq!(format!("{}/rustlavel-chart.js", charts.prefix), INIT_JS_PATH);
130 }
131
132 #[test]
133 fn a_custom_mount_drops_a_trailing_slash() {
134 assert_eq!(Charts::at("/static/charts/").prefix, "/static/charts");
135 }
136}