Skip to content

Commit 972432d

Browse files
GuillaumeGomezsyphar
authored andcommitted
Put SVG images into CSS
1 parent a5ca9aa commit 972432d

25 files changed

+283
-143
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ once_cell = { version = "1.4.0", features = ["parking_lot"] }
5454
base64 = "0.22"
5555
strum = { version = "0.26.1", features = ["derive"] }
5656
lol_html = "1.0.0"
57-
font-awesome-as-a-crate = { path = "crates/font-awesome-as-a-crate" }
5857
dashmap = "6.0.0"
5958
string_cache = "0.8.0"
6059
zip = {version = "2.2.0", default-features = false, features = ["bzip2"]}
@@ -136,6 +135,7 @@ anyhow = { version = "1.0.42", features = ["backtrace"] }
136135
grass = { version = "0.13.1", default-features = false }
137136
once_cell = { version = "1.4.0", features = ["parking_lot"] }
138137
syntect = { version = "5.0.0", default-features = false, features = ["parsing", "dump-create", "yaml-load", "regex-onig"] }
138+
font-awesome-as-a-crate = { path = "crates/font-awesome-as-a-crate" }
139139

140140
[[bench]]
141141
name = "compression"

build.rs

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
use anyhow::{Context as _, Error, Result};
2-
use std::{env, path::Path};
2+
use font_awesome_as_a_crate as f_a;
3+
use std::{
4+
env,
5+
path::{Path, PathBuf},
6+
};
37

48
mod tracked {
59
use once_cell::sync::Lazy;
@@ -80,11 +84,188 @@ fn main() -> Result<()> {
8084
write_known_targets(out_dir)?;
8185
compile_syntax(out_dir).context("could not compile syntax files")?;
8286

87+
println!("cargo::rustc-check-cfg=cfg(icons_out_dir)");
88+
println!("cargo:rustc-cfg=icons_out_dir");
89+
90+
let package_dir = env::var("CARGO_MANIFEST_DIR").context("missing CARGO_MANIFEST_DIR")?;
91+
let package_dir = Path::new(&package_dir);
92+
generate_css_icons(package_dir.join("static/icons.css"), out_dir)?;
93+
8394
// trigger recompilation when a new migration is added
8495
println!("cargo:rerun-if-changed=migrations");
8596
Ok(())
8697
}
8798

99+
fn capitalize(s: &str) -> String {
100+
let mut c = s.chars();
101+
match c.next() {
102+
None => String::new(),
103+
Some(f) => f.to_uppercase().chain(c).collect(),
104+
}
105+
}
106+
107+
fn render_icon(
108+
icon_name: &str,
109+
icon_str: &str,
110+
type_name: String,
111+
code_output: &mut String,
112+
css_output: &mut String,
113+
icon_kind: &str,
114+
) {
115+
let css_class = format!("f-a_{icon_name}_{icon_kind}");
116+
css_output.push_str(&format!(
117+
"\
118+
.{css_class} {{
119+
--svg_{icon_name}_{icon_kind}: url('data:image/svg+xml,{icon_str}');
120+
-webkit-mask: var(--svg_{icon_name}_{icon_kind}) no-repeat center;
121+
mask: var(--svg_{icon_name}_{icon_kind}) no-repeat center;
122+
}}
123+
",
124+
));
125+
let type_name = format!("{type_name}{}", capitalize(icon_kind));
126+
code_output.push_str(&format!(
127+
r#"#[derive(Clone, Copy, Debug, PartialEq, Eq)]
128+
pub struct {type_name};
129+
impl {type_name} {{
130+
pub fn render(&self, fw: bool, spin: bool, extra: &str) -> rinja::filters::Safe<String> {{
131+
render({css_class:?}, fw, spin, extra)
132+
}}
133+
}}
134+
"#,
135+
));
136+
}
137+
138+
fn generate_css_icons(css_path: PathBuf, out_dir: &Path) -> Result<()> {
139+
let mut code_output = r#"pub(crate) mod icons {
140+
fn render(
141+
css_class: &str,
142+
fw: bool,
143+
spin: bool,
144+
extra: &str,
145+
) -> rinja::filters::Safe<String> {
146+
let mut classes = vec!["fa-svg"];
147+
if fw {
148+
classes.push("fa-svg-fw");
149+
}
150+
if spin {
151+
classes.push("fa-svg-spin");
152+
}
153+
if !extra.is_empty() {
154+
classes.push(extra);
155+
}
156+
let icon = format!(
157+
"<span class=\"{css_class} {class}\" aria-hidden=\"true\"></span>",
158+
class = classes.join(" "),
159+
);
160+
161+
rinja::filters::Safe(icon)
162+
}"#
163+
.to_string();
164+
let mut css_output = r#".svg-clipboard {
165+
/* This icon is copied from crates.io */
166+
--svg-clipboard: url('data:image/svg+xml,<svg width="24" height="25" viewBox="0 0 24 25" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-label="Copy to clipboard"><path d="M18 20h2v3c0 1-1 2-2 2H2c-.998 0-2-1-2-2V5c0-.911.755-1.667 1.667-1.667h5A3.323 3.323 0 0110 0a3.323 3.323 0 013.333 3.333h5C19.245 3.333 20 4.09 20 5v8.333h-2V9H2v14h16v-3zM3 7h14c0-.911-.793-1.667-1.75-1.667H13.5c-.957 0-1.75-.755-1.75-1.666C11.75 2.755 10.957 2 10 2s-1.75.755-1.75 1.667c0 .911-.793 1.666-1.75 1.666H4.75C3.793 5.333 3 6.09 3 7z"/><path d="M4 19h6v2H4zM12 11H4v2h8zM4 17h4v-2H4zM15 15v-3l-4.5 4.5L15 21v-3l8.027-.032L23 15z"/></svg>');
167+
-webkit-mask: var(--svg-clipboard) no-repeat center;
168+
mask: var(--svg-clipboard) no-repeat center;
169+
}"#.to_string();
170+
171+
let brands: &[&dyn f_a::Brands] = &[
172+
&f_a::icons::IconFonticons,
173+
&f_a::icons::IconRust,
174+
&f_a::icons::IconMarkdown,
175+
&f_a::icons::IconGitAlt,
176+
];
177+
let regular: &[&dyn f_a::Regular] = &[
178+
&f_a::icons::IconFileLines,
179+
&f_a::icons::IconFolderOpen,
180+
&f_a::icons::IconFile,
181+
&f_a::icons::IconStar,
182+
];
183+
let solid: &[&dyn f_a::Solid] = &[
184+
&f_a::icons::IconCircleInfo,
185+
&f_a::icons::IconGears,
186+
&f_a::icons::IconTable,
187+
&f_a::icons::IconRoad,
188+
&f_a::icons::IconDownload,
189+
&f_a::icons::IconCubes,
190+
&f_a::icons::IconSquareRss,
191+
&f_a::icons::IconFileLines,
192+
&f_a::icons::IconCheck,
193+
&f_a::icons::IconTriangleExclamation,
194+
&f_a::icons::IconGear,
195+
&f_a::icons::IconX,
196+
&f_a::icons::IconHouse,
197+
&f_a::icons::IconCodeBranch,
198+
&f_a::icons::IconStar,
199+
&f_a::icons::IconCircleExclamation,
200+
&f_a::icons::IconCube,
201+
&f_a::icons::IconChevronLeft,
202+
&f_a::icons::IconChevronRight,
203+
&f_a::icons::IconFolderOpen,
204+
&f_a::icons::IconLock,
205+
&f_a::icons::IconFlag,
206+
&f_a::icons::IconBook,
207+
&f_a::icons::IconMagnifyingGlass,
208+
&f_a::icons::IconLeaf,
209+
&f_a::icons::IconChartLine,
210+
&f_a::icons::IconList,
211+
&f_a::icons::IconUser,
212+
&f_a::icons::IconTrash,
213+
&f_a::icons::IconArrowLeft,
214+
&f_a::icons::IconArrowRight,
215+
&f_a::icons::IconLink,
216+
&f_a::icons::IconScaleUnbalancedFlip,
217+
&f_a::icons::IconSpinner,
218+
];
219+
220+
for icon in brands {
221+
render_icon(
222+
icon.icon_name(),
223+
icon.icon_str(),
224+
format!("{icon:?}"),
225+
&mut code_output,
226+
&mut css_output,
227+
"brands",
228+
);
229+
}
230+
for icon in regular {
231+
render_icon(
232+
icon.icon_name(),
233+
icon.icon_str(),
234+
format!("{icon:?}"),
235+
&mut code_output,
236+
&mut css_output,
237+
"regular",
238+
);
239+
}
240+
for icon in solid {
241+
render_icon(
242+
icon.icon_name(),
243+
icon.icon_str(),
244+
format!("{icon:?}"),
245+
&mut code_output,
246+
&mut css_output,
247+
"solid",
248+
);
249+
}
250+
251+
std::fs::write(&css_path, css_output).map_err(|error| {
252+
Error::msg(format!(
253+
"Failed to write into `{}`: {error:?}",
254+
css_path.display()
255+
))
256+
})?;
257+
258+
code_output.push('}');
259+
let icons_file = out_dir.join("icons.rs");
260+
std::fs::write(&icons_file, code_output).map_err(|error| {
261+
Error::msg(format!(
262+
"Failed to write `{}`: {error:?}",
263+
icons_file.display()
264+
))
265+
})?;
266+
Ok(())
267+
}
268+
88269
fn write_git_version(out_dir: &Path) -> Result<()> {
89270
let maybe_hash = get_git_hash()?;
90271
let git_hash = maybe_hash.as_deref().unwrap_or("???????");

crates/font-awesome-as-a-crate/src/lib.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ If you have problems, [contact us](https://github.com/rust-lang/docs.rs/issues),
66
*/
77

88
use std::error::Error;
9-
use std::fmt::{self, Display, Formatter};
9+
use std::fmt::{self, Debug, Display, Formatter};
1010

1111
#[cfg(font_awesome_out_dir)]
1212
include!(concat!(env!("OUT_DIR"), "/fontawesome.rs"));
@@ -94,18 +94,18 @@ pub trait IconStr {
9494
fn icon_str(&self) -> &'static str;
9595
}
9696

97-
pub trait Brands: IconStr {
98-
fn get_type() -> Type {
97+
pub trait Brands: IconStr + Debug {
98+
fn get_type(&self) -> Type {
9999
Type::Brands
100100
}
101101
}
102-
pub trait Regular: IconStr {
103-
fn get_type() -> Type {
102+
pub trait Regular: IconStr + Debug {
103+
fn get_type(&self) -> Type {
104104
Type::Regular
105105
}
106106
}
107-
pub trait Solid: IconStr {
108-
fn get_type() -> Type {
107+
pub trait Solid: IconStr + Debug {
108+
fn get_type(&self) -> Type {
109109
Type::Solid
110110
}
111111
}

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ pub use self::registry_api::RegistryApi;
1313
pub use self::storage::{AsyncStorage, Storage};
1414
pub use self::web::{start_background_metrics_webserver, start_web_server};
1515

16-
pub(crate) use font_awesome_as_a_crate as f_a;
16+
#[cfg(icons_out_dir)]
17+
include!(concat!(env!("OUT_DIR"), "/icons.rs"));
18+
#[cfg(not(icons_out_dir))]
19+
include!("icons.rs");
1720

1821
mod build_queue;
1922
pub mod cdn;

src/web/csp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl Csp {
5454
// the MIME type to allow loading favicons.
5555
//
5656
// Images from other HTTPS origins are also temporary allowed until issue #66 is fixed.
57-
result.push_str("; img-src 'self' https:");
57+
result.push_str("; img-src 'self' https: data:");
5858

5959
match content_type {
6060
ContentType::Html => self.render_html(&mut result),

src/web/page/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ pub(crate) struct GlobalAlert {
88
pub(crate) url: &'static str,
99
pub(crate) text: &'static str,
1010
pub(crate) css_class: &'static str,
11-
pub(crate) fa_icon: crate::f_a::icons::IconTriangleExclamation,
11+
pub(crate) fa_icon: crate::icons::IconTriangleExclamationSolid,
1212
}

src/web/page/templates.rs

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -200,33 +200,6 @@ pub mod filters {
200200
Ok(unindented)
201201
}
202202

203-
pub fn fas<T: font_awesome_as_a_crate::Solid>(
204-
value: T,
205-
fw: bool,
206-
spin: bool,
207-
extra: &str,
208-
) -> rinja::Result<Safe<String>> {
209-
super::render_icon(value.icon_str(), fw, spin, extra)
210-
}
211-
212-
pub fn far<T: font_awesome_as_a_crate::Regular>(
213-
value: T,
214-
fw: bool,
215-
spin: bool,
216-
extra: &str,
217-
) -> rinja::Result<Safe<String>> {
218-
super::render_icon(value.icon_str(), fw, spin, extra)
219-
}
220-
221-
pub fn fab<T: font_awesome_as_a_crate::Brands>(
222-
value: T,
223-
fw: bool,
224-
spin: bool,
225-
extra: &str,
226-
) -> rinja::Result<Safe<String>> {
227-
super::render_icon(value.icon_str(), fw, spin, extra)
228-
}
229-
230203
pub fn highlight(code: impl std::fmt::Display, lang: &str) -> rinja::Result<Safe<String>> {
231204
let highlighted_code = crate::web::highlight::with_lang(Some(lang), &code.to_string());
232205
Ok(Safe(format!(
@@ -254,28 +227,3 @@ pub mod filters {
254227
))
255228
}
256229
}
257-
258-
fn render_icon(
259-
icon_str: &str,
260-
fw: bool,
261-
spin: bool,
262-
extra: &str,
263-
) -> rinja::Result<rinja::filters::Safe<String>> {
264-
let mut classes = vec!["fa-svg"];
265-
if fw {
266-
classes.push("fa-svg-fw");
267-
}
268-
if spin {
269-
classes.push("fa-svg-spin");
270-
}
271-
if !extra.is_empty() {
272-
classes.push(extra);
273-
}
274-
let icon = format!(
275-
"\
276-
<span class=\"{class}\" aria-hidden=\"true\">{icon_str}</span>",
277-
class = classes.join(" "),
278-
);
279-
280-
Ok(rinja::filters::Safe(icon))
281-
}

templates/about-base.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,27 @@
1111
<h1 id="crate-title" class="no-description">Docs.rs documentation</h1>
1212
<div class="pure-menu pure-menu-horizontal">
1313
<ul class="pure-menu-list">
14-
{% set text = crate::f_a::icons::IconCircleInfo|fas(false, false, "") %}
14+
{% set text = crate::icons::IconCircleInfoSolid.render(false, false, "") %}
1515
{% set text = "{} <span class='title'>About</span>"|format(text) %}
1616
{% call macros::active_link(expected="index", href="/about", text=text) %}
1717

18-
{% set text = crate::f_a::icons::IconFonticons|fab(false, false, "") %}
18+
{% set text = crate::icons::IconFonticonsBrands.render(false, false, "") %}
1919
{% set text = "{} <span class='title'>Badges</span>"|format(text) %}
2020
{% call macros::active_link(expected="badges", href="/about/badges", text=text) %}
2121

22-
{% set text = crate::f_a::icons::IconGears|fas(false, false, "") %}
22+
{% set text = crate::icons::IconGearsSolid.render(false, false, "") %}
2323
{% set text = "{} <span class='title'>Builds</span>"|format(text) %}
2424
{% call macros::active_link(expected="builds", href="/about/builds", text=text) %}
2525

26-
{% set text = crate::f_a::icons::IconTable|fas(false, false, "") %}
26+
{% set text = crate::icons::IconTableSolid.render(false, false, "") %}
2727
{% set text = "{} <span class='title'>Metadata</span>"|format(text) %}
2828
{% call macros::active_link(expected="metadata", href="/about/metadata", text=text) %}
2929

30-
{% set text = crate::f_a::icons::IconRoad|fas(false, false, "") %}
30+
{% set text = crate::icons::IconRoadSolid.render(false, false, "") %}
3131
{% set text = "{} <span class='title'>Shorthand URLs</span>"|format(text) %}
3232
{% call macros::active_link(expected="redirections", href="/about/redirections", text=text) %}
3333

34-
{% set text = crate::f_a::icons::IconDownload|fas(false, false, "") %}
34+
{% set text = crate::icons::IconDownloadSolid.render(false, false, "") %}
3535
{% set text = "{} <span class='title'>Download</span>"|format(text) %}
3636
{% call macros::active_link(expected="download", href="/about/download", text=text) %}
3737
</ul>

templates/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
{%- set build_slug = slug::slugify(crate::BUILD_VERSION) -%}
1212
<link rel="stylesheet" href="/-/static/vendored.css?{{ build_slug }}" media="all" />
1313
<link rel="stylesheet" href="/-/static/style.css?{{ build_slug }}" media="all" />
14+
<link rel="stylesheet" href="/-/static/icons.css?{{ build_slug }}" media="all" />
1415

1516
<link rel="search" href="/-/static/opensearch.xml" type="application/opensearchdescription+xml" title="Docs.rs" />
1617

0 commit comments

Comments
 (0)