forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstatic_files.rs
172 lines (157 loc) · 6.87 KB
/
static_files.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! Static files bundled with documentation output.
//!
//! All the static files are included here for centralized access in case anything other than the
//! HTML rendering code (say, the theme checker) needs to access one of these files.
use rustc_data_structures::fx::{FxHashMap, FxHasher};
use std::borrow::Cow;
use std::hash::Hasher;
use std::path::{Path, PathBuf};
use std::{fmt, str};
pub(crate) struct StaticFile {
pub(crate) filename: PathBuf,
pub(crate) bytes: Cow<'static, [u8]>,
}
impl StaticFile {
fn new(
filename: &str,
bytes: &'static [u8],
file_map: &mut FxHashMap<String, String>,
) -> StaticFile {
// For now, only `rustdoc.css` style file needs this mechanism but it can be extended
// pretty easily by changing this condition.
if filename.ends_with("/rustdoc.css") {
let bytes = replace_static_files_include(bytes, file_map);
Self { filename: static_filename(filename, &bytes), bytes: Cow::Owned(bytes) }
} else {
let ret =
Self { filename: static_filename(filename, bytes), bytes: Cow::Borrowed(bytes) };
let filename = Path::new(filename).file_name().unwrap().to_str().unwrap().to_string();
file_map
.insert(filename, ret.filename.file_name().unwrap().to_str().unwrap().to_string());
ret
}
}
pub(crate) fn minified(&self) -> Vec<u8> {
let extension = match self.filename.extension() {
Some(e) => e,
None => return self.bytes.to_vec(),
};
if extension == "css" {
minifier::css::minify(str::from_utf8(&self.bytes).unwrap()).unwrap().to_string().into()
} else if extension == "js" {
minifier::js::minify(str::from_utf8(&self.bytes).unwrap()).to_string().into()
} else {
self.bytes.to_vec()
}
}
pub(crate) fn output_filename(&self) -> &Path {
&self.filename
}
}
/// The Display implementation for a StaticFile outputs its filename. This makes it
/// convenient to interpolate static files into HTML templates.
impl fmt::Display for StaticFile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.output_filename().display())
}
}
/// This function goes through the CSS content and replaces all content wrapped between:
/// `/* REPLACE {content} */` (where `{content}` is what will be replaced.)
fn replace_static_files_include(bytes: &[u8], file_map: &FxHashMap<String, String>) -> Vec<u8> {
let bytes = str::from_utf8(bytes).unwrap();
let mut it = bytes.split("/* REPLACE ");
let mut content = String::with_capacity(bytes.len());
while let Some(entry) = it.next() {
if content.is_empty() {
content.push_str(entry);
continue;
}
let mut parts = entry.splitn(2, " */");
let file = parts.next().unwrap();
let rest = parts.next().unwrap();
content.push_str(file_map.get(file).unwrap());
content.push_str(rest);
}
content.into()
}
/// Insert the provided suffix into a filename just before the extension.
pub(crate) fn suffix_path(filename: &str, suffix: &str) -> PathBuf {
// We use splitn vs Path::extension here because we might get a filename
// like `style.min.css` and we want to process that into
// `style-suffix.min.css`. Path::extension would just return `css`
// which would result in `style.min-suffix.css` which isn't what we
// want.
let (base, ext) = filename.split_once('.').unwrap();
let filename = format!("{}{}.{}", base, suffix, ext);
filename.into()
}
pub(crate) fn static_filename(filename: &str, contents: &[u8]) -> PathBuf {
let filename = filename.rsplit('/').next().unwrap();
suffix_path(filename, &static_suffix(contents))
}
fn static_suffix(bytes: &[u8]) -> String {
let mut hasher = FxHasher::default();
hasher.write(bytes);
format!("-{:016x}", hasher.finish())
}
macro_rules! static_files {
($($field:ident => $file_path:literal,)+) => {
pub(crate) struct StaticFiles {
$(pub $field: StaticFile,)+
}
pub(crate) static STATIC_FILES: std::sync::LazyLock<StaticFiles> = std::sync::LazyLock::new(|| {
let mut map = FxHashMap::default();
StaticFiles {
$($field: StaticFile::new($file_path, include_bytes!($file_path), &mut map),)+
}
});
pub(crate) fn for_each<E>(f: impl Fn(&StaticFile) -> Result<(), E>) -> Result<(), E> {
for sf in [
$(&STATIC_FILES.$field,)+
] {
f(sf)?
}
Ok(())
}
}
}
static_files! {
main_js => "static/js/main.js",
search_js => "static/js/search.js",
settings_js => "static/js/settings.js",
source_script_js => "static/js/source-script.js",
storage_js => "static/js/storage.js",
scrape_examples_js => "static/js/scrape-examples.js",
wheel_svg => "static/images/wheel.svg",
clipboard_svg => "static/images/clipboard.svg",
copyright => "static/COPYRIGHT.txt",
license_apache => "static/LICENSE-APACHE.txt",
license_mit => "static/LICENSE-MIT.txt",
rust_logo_svg => "static/images/rust-logo.svg",
rust_favicon_svg => "static/images/favicon.svg",
rust_favicon_png_16 => "static/images/favicon-16x16.png",
rust_favicon_png_32 => "static/images/favicon-32x32.png",
theme_light_css => "static/css/themes/light.css",
theme_dark_css => "static/css/themes/dark.css",
theme_ayu_css => "static/css/themes/ayu.css",
fira_sans_regular => "static/fonts/FiraSans-Regular.woff2",
fira_sans_medium => "static/fonts/FiraSans-Medium.woff2",
fira_sans_license => "static/fonts/FiraSans-LICENSE.txt",
source_serif_4_regular => "static/fonts/SourceSerif4-Regular.ttf.woff2",
source_serif_4_bold => "static/fonts/SourceSerif4-Bold.ttf.woff2",
source_serif_4_italic => "static/fonts/SourceSerif4-It.ttf.woff2",
source_serif_4_license => "static/fonts/SourceSerif4-LICENSE.md",
source_code_pro_regular => "static/fonts/SourceCodePro-Regular.ttf.woff2",
source_code_pro_semibold => "static/fonts/SourceCodePro-Semibold.ttf.woff2",
source_code_pro_italic => "static/fonts/SourceCodePro-It.ttf.woff2",
source_code_pro_license => "static/fonts/SourceCodePro-LICENSE.txt",
nanum_barun_gothic_regular => "static/fonts/NanumBarunGothic.ttf.woff2",
nanum_barun_gothic_license => "static/fonts/NanumBarunGothic-LICENSE.txt",
// It's important for the CSS files to be the last since we need to replace some of their
// content (the static file names).
rustdoc_css => "static/css/rustdoc.css",
settings_css => "static/css/settings.css",
noscript_css => "static/css/noscript.css",
normalize_css => "static/css/normalize.css",
}
pub(crate) static SCRAPE_EXAMPLES_HELP_MD: &str = include_str!("static/scrape-examples-help.md");