diff --git a/crates/font-awesome-as-a-crate/build.rs b/crates/font-awesome-as-a-crate/build.rs index 7634058c0..0a811a09e 100644 --- a/crates/font-awesome-as-a-crate/build.rs +++ b/crates/font-awesome-as-a-crate/build.rs @@ -1,5 +1,7 @@ use std::{ + collections::HashMap, env, + fmt::Write as FmtWrite, fs::{read_dir, File}, io::{Read, Write}, path::Path, @@ -11,13 +13,26 @@ fn main() { write_fontawesome_sprite(); } +fn capitalize_first_letter(s: &str) -> String { + let mut c = s.chars(); + match c.next() { + None => String::new(), + Some(f) => f.to_uppercase().chain(c).collect(), + } +} + fn write_fontawesome_sprite() { + let mut types = HashMap::new(); let dest_path = Path::new(&env::var("OUT_DIR").unwrap()).join("fontawesome.rs"); let mut dest_file = File::create(dest_path).unwrap(); dest_file .write_all(b"const fn fontawesome_svg(dir:&str,file:&str)->&'static str{match(dir.as_bytes(),file.as_bytes()){") .expect("fontawesome fn write"); - for dirname in &["brands", "regular", "solid"] { + for (dirname, trait_name) in &[ + ("brands", "Brands"), + ("regular", "Regular"), + ("solid", "Solid"), + ] { let dir = read_dir(Path::new("fontawesome-free-6.2.0-desktop/svgs").join(dirname)).unwrap(); let mut data = String::new(); for file in dir { @@ -32,20 +47,45 @@ fn write_fontawesome_sprite() { .expect("fontawesome file read"); // if this assert goes off, add more hashes here and in the format! below assert!(!data.contains("###"), "file {filename} breaks raw string"); + let filename = filename.replace(".svg", ""); dest_file .write_all( - format!( - r####"(b"{dirname}",b"{filename}")=>r#"{data}"#,"####, - data = data, - dirname = dirname, - filename = filename.replace(".svg", ""), - ) - .as_bytes(), + format!(r####"(b"{dirname}",b"{filename}")=>r#"{data}"#,"####).as_bytes(), ) .expect("write fontawesome file"); + types + .entry(filename) + .or_insert_with(|| (data.clone(), Vec::with_capacity(3))) + .1 + .push(trait_name); } } dest_file - .write_all(b"_=>\"\"}}") + .write_all(b"_=>\"\"}} pub mod icons { use super::{IconStr, Regular, Brands, Solid};") .expect("fontawesome fn write"); + + for (icon, (data, kinds)) in types { + let mut type_name = "Icon".to_string(); + type_name.extend(icon.split('-').map(capitalize_first_letter)); + let kinds = kinds.iter().fold(String::new(), |mut acc, k| { + let _ = writeln!(acc, "impl {k} for {type_name} {{}}"); + acc + }); + dest_file + .write_all( + format!( + "\n#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct {type_name}; +impl IconStr for {type_name} {{ + fn icon_name(&self) -> &'static str {{ r#\"{icon}\"# }} + fn icon_str(&self) -> &'static str {{ r#\"{data}\"# }} +}} +{kinds}" + ) + .as_bytes(), + ) + .expect("write fontawesome file types"); + } + + dest_file.write_all(b"}").expect("fontawesome fn write"); } diff --git a/crates/font-awesome-as-a-crate/src/lib.rs b/crates/font-awesome-as-a-crate/src/lib.rs index 16c0297dd..4afab7d18 100644 --- a/crates/font-awesome-as-a-crate/src/lib.rs +++ b/crates/font-awesome-as-a-crate/src/lib.rs @@ -87,6 +87,29 @@ pub const fn svg(type_: Type, name: &str) -> Result<&'static str, NameError> { Ok(svg) } +pub trait IconStr { + /// Name of the icon, like "triangle-exclamation". + fn icon_name(&self) -> &'static str; + /// The SVG content of the icon. + fn icon_str(&self) -> &'static str; +} + +pub trait Brands: IconStr { + fn get_type() -> Type { + Type::Brands + } +} +pub trait Regular: IconStr { + fn get_type() -> Type { + Type::Regular + } +} +pub trait Solid: IconStr { + fn get_type() -> Type { + Type::Solid + } +} + #[cfg(test)] mod tests { const fn usable_as_const_() { diff --git a/src/lib.rs b/src/lib.rs index 8fe4904f1..6036c74f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,8 @@ pub use self::registry_api::RegistryApi; pub use self::storage::{AsyncStorage, Storage}; pub use self::web::{start_background_metrics_webserver, start_web_server}; +pub(crate) use font_awesome_as_a_crate as f_a; + mod build_queue; pub mod cdn; mod config; diff --git a/src/web/page/mod.rs b/src/web/page/mod.rs index 200b9f6a0..2de262813 100644 --- a/src/web/page/mod.rs +++ b/src/web/page/mod.rs @@ -3,37 +3,10 @@ pub(crate) mod web_page; pub(crate) use templates::TemplateData; -use serde::Serialize; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub(crate) struct GlobalAlert { pub(crate) url: &'static str, pub(crate) text: &'static str, pub(crate) css_class: &'static str, - pub(crate) fa_icon: &'static str, -} - -#[cfg(test)] -mod tera_tests { - use super::*; - use serde_json::json; - - #[test] - fn serialize_global_alert() { - let alert = GlobalAlert { - url: "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", - text: "THE WORLD WILL SOON END", - css_class: "THE END IS NEAR", - fa_icon: "https://gph.is/1uOvmqR", - }; - - let correct_json = json!({ - "url": "http://www.hasthelargehadroncolliderdestroyedtheworldyet.com/", - "text": "THE WORLD WILL SOON END", - "css_class": "THE END IS NEAR", - "fa_icon": "https://gph.is/1uOvmqR" - }); - - assert_eq!(correct_json, serde_json::to_value(alert).unwrap()); - } + pub(crate) fa_icon: crate::f_a::icons::IconTriangleExclamation, } diff --git a/src/web/page/templates.rs b/src/web/page/templates.rs index c3bfb4193..898a18126 100644 --- a/src/web/page/templates.rs +++ b/src/web/page/templates.rs @@ -1,8 +1,8 @@ use crate::error::Result; use crate::web::rustdoc::RustdocPage; -use anyhow::{anyhow, Context}; +use anyhow::Context; use rinja::Template; -use std::{fmt, sync::Arc}; +use std::sync::Arc; use tracing::trace; #[derive(Template)] @@ -88,7 +88,6 @@ impl TemplateData { } pub mod filters { - use super::IconType; use chrono::{DateTime, Utc}; use rinja::filters::Safe; use std::borrow::Cow; @@ -201,16 +200,31 @@ pub mod filters { Ok(unindented) } - pub fn fas(value: &str, fw: bool, spin: bool, extra: &str) -> rinja::Result> { - IconType::Strong.render(value, fw, spin, extra).map(Safe) + pub fn fas( + value: T, + fw: bool, + spin: bool, + extra: &str, + ) -> rinja::Result> { + super::render_icon(value.icon_str(), fw, spin, extra) } - pub fn far(value: &str, fw: bool, spin: bool, extra: &str) -> rinja::Result> { - IconType::Regular.render(value, fw, spin, extra).map(Safe) + pub fn far( + value: T, + fw: bool, + spin: bool, + extra: &str, + ) -> rinja::Result> { + super::render_icon(value.icon_str(), fw, spin, extra) } - pub fn fab(value: &str, fw: bool, spin: bool, extra: &str) -> rinja::Result> { - IconType::Brand.render(value, fw, spin, extra).map(Safe) + pub fn fab( + value: T, + fw: bool, + spin: bool, + extra: &str, + ) -> rinja::Result> { + super::render_icon(value.icon_str(), fw, spin, extra) } pub fn highlight(code: impl std::fmt::Display, lang: &str) -> rinja::Result> { @@ -241,59 +255,27 @@ pub mod filters { } } -enum IconType { - Strong, - Regular, - Brand, -} - -impl fmt::Display for IconType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let icon = match self { - Self::Strong => "solid", - Self::Regular => "regular", - Self::Brand => "brands", - }; - - f.write_str(icon) +fn render_icon( + icon_str: &str, + fw: bool, + spin: bool, + extra: &str, +) -> rinja::Result> { + let mut classes = vec!["fa-svg"]; + if fw { + classes.push("fa-svg-fw"); } -} - -impl IconType { - fn render(self, icon_name: &str, fw: bool, spin: bool, extra: &str) -> rinja::Result { - let type_ = match self { - IconType::Strong => font_awesome_as_a_crate::Type::Solid, - IconType::Regular => font_awesome_as_a_crate::Type::Regular, - IconType::Brand => font_awesome_as_a_crate::Type::Brands, - }; - - let icon_file_string = font_awesome_as_a_crate::svg(type_, icon_name).map_err(|err| { - rinja::Error::Custom( - anyhow!(err) - .context(format!( - "error trying to render icon with name \"{}\" and type \"{}\"", - icon_name, type_, - )) - .into(), - ) - })?; - - let mut classes = vec!["fa-svg"]; - if fw { - classes.push("fa-svg-fw"); - } - if spin { - classes.push("fa-svg-spin"); - } - if !extra.is_empty() { - classes.push(extra); - } - let icon = format!( - "\ -{icon_file_string}", - class = classes.join(" "), - ); - - Ok(icon) + if spin { + classes.push("fa-svg-spin"); } + if !extra.is_empty() { + classes.push(extra); + } + let icon = format!( + "\ +{icon_str}", + class = classes.join(" "), + ); + + Ok(rinja::filters::Safe(icon)) } diff --git a/templates/about-base.html b/templates/about-base.html index ae9381fbb..93e40fbad 100644 --- a/templates/about-base.html +++ b/templates/about-base.html @@ -11,27 +11,27 @@

Docs.rs documentation

    - {% set text = "circle-info"|fas(false, false, "") %} + {% set text = crate::f_a::icons::IconCircleInfo|fas(false, false, "") %} {% set text = "{} About"|format(text) %} {% call macros::active_link(expected="index", href="/about", text=text) %} - {% set text = "fonticons"|fab(false, false, "") %} + {% set text = crate::f_a::icons::IconFonticons|fab(false, false, "") %} {% set text = "{} Badges"|format(text) %} {% call macros::active_link(expected="badges", href="/about/badges", text=text) %} - {% set text = "gears"|fas(false, false, "") %} + {% set text = crate::f_a::icons::IconGears|fas(false, false, "") %} {% set text = "{} Builds"|format(text) %} {% call macros::active_link(expected="builds", href="/about/builds", text=text) %} - {% set text = "table"|fas(false, false, "") %} + {% set text = crate::f_a::icons::IconTable|fas(false, false, "") %} {% set text = "{} Metadata"|format(text) %} {% call macros::active_link(expected="metadata", href="/about/metadata", text=text) %} - {% set text = "road"|fas(false, false, "") %} + {% set text = crate::f_a::icons::IconRoad|fas(false, false, "") %} {% set text = "{} Shorthand URLs"|format(text) %} {% call macros::active_link(expected="redirections", href="/about/redirections", text=text) %} - {% set text = "download"|fas(false, false, "") %} + {% set text = crate::f_a::icons::IconDownload|fas(false, false, "") %} {% set text = "{} Download"|format(text) %} {% call macros::active_link(expected="download", href="/about/download", text=text) %}
diff --git a/templates/core/home.html b/templates/core/home.html index ac55e089f..9715eaf2b 100644 --- a/templates/core/home.html +++ b/templates/core/home.html @@ -12,7 +12,7 @@ {%- block body -%}
-

{{ "cubes"|fas(false, false, "") }} Docs.rs

+

{{ crate::f_a::icons::IconCubes|fas(false, false, "") }} Docs.rs

@@ -36,7 +36,7 @@

{{ "cubes"|fas(false, false, "") }} Docs.rs

Recent Releases - {{ "square-rss"|fas(false, false, "") }} + {{ crate::f_a::icons::IconSquareRss|fas(false, false, "") }}
diff --git a/templates/crate/build_details.html b/templates/crate/build_details.html index bdfc55179..fa6fb2cdd 100644 --- a/templates/crate/build_details.html +++ b/templates/crate/build_details.html @@ -30,7 +30,7 @@
  • -
    {{ "file-lines"|fas(false, false, "") }}
    +
    {{ crate::f_a::icons::IconFileLines|fas(false, false, "") }}
    {% if current_filename.as_deref().unwrap_or_default() == filename.as_str() %} {{ filename }} diff --git a/templates/crate/builds.html b/templates/crate/builds.html index 4acc6ba5b..4b61a564b 100644 --- a/templates/crate/builds.html +++ b/templates/crate/builds.html @@ -44,13 +44,13 @@
    {%- if build.build_status == "success" -%} - {{ "check"|fas(false, false, "") }} + {{ crate::f_a::icons::IconCheck|fas(false, false, "") }} {%- elif build.build_status == "failure" -%} - {{ "triangle-exclamation"|fas(false, false, "") }} + {{ crate::f_a::icons::IconTriangleExclamation|fas(false, false, "") }} {%- elif build.build_status == "in_progress" -%} - {{ "gear"|fas(false, true, "") }} + {{ crate::f_a::icons::IconGear|fas(false, true, "") }} {%- else -%} - {{ "x"|fas(false, false, "") }} + {{ crate::f_a::icons::IconX|fas(false, false, "") }} {%- endif -%}
    diff --git a/templates/crate/details.html b/templates/crate/details.html index 28c568145..dad034034 100644 --- a/templates/crate/details.html +++ b/templates/crate/details.html @@ -43,7 +43,7 @@ {%- if let Some(homepage_url) = homepage_url -%}
  • - {{ "house"|fas(false, false, "") }} Homepage + {{ crate::f_a::icons::IconHouse|fas(false, false, "") }} Homepage
  • {%- endif -%} @@ -52,7 +52,7 @@ {%- if let Some(documentation_url) = documentation_url -%}
  • - {{ "file-lines"|far(false, false, "") }} Documentation + {{ crate::f_a::icons::IconFileLines|far(false, false, "") }} Documentation
  • {%- endif -%} @@ -64,20 +64,20 @@ {# If the repo link is for github or gitlab, show some stats #} {# TODO: add support for hosts besides github and gitlab (#35) #} {%- if let Some(repository_metadata) = repository_metadata -%} - {{ "code-branch"|fas(false, false, "") }} + {{ crate::f_a::icons::IconCodeBranch|fas(false, false, "") }} {% if let Some(name) = repository_metadata.name %} {{name}} {% else %} Repository {% endif %}
    - {{ "star"|fas(false, false, "left-margin") }} {{ repository_metadata.stars }} - {{ "code-branch"|fas(false, false, "") }} {{ repository_metadata.forks }} - {{ "circle-exclamation"|fas(false, false, "") }} {{ repository_metadata.issues }} + {{ crate::f_a::icons::IconStar|fas(false, false, "left-margin") }} {{ repository_metadata.stars }} + {{ crate::f_a::icons::IconCodeBranch|fas(false, false, "") }} {{ repository_metadata.forks }} + {{ crate::f_a::icons::IconCircleExclamation|fas(false, false, "") }} {{ repository_metadata.issues }} {# If the repo link is unknown, just show a normal link #} {%- else -%} - {{ "code-branch"|fas(false, false, "") }} Repository + {{ crate::f_a::icons::IconCodeBranch|fas(false, false, "") }} Repository {%- endif -%} @@ -87,7 +87,7 @@
  • - {{ "cube"|fas(false, false, "") }} crates.io + {{ crate::f_a::icons::IconCube|fas(false, false, "") }} crates.io
  • @@ -160,7 +160,7 @@
    {%- elif build_status == "in_progress" -%}
    - {{ "gear"|fas(false, true, "") }} + {{ crate::f_a::icons::IconGear|fas(false, true, "") }} Build is in progress, it will be available soon
    {%- endif -%} diff --git a/templates/crate/source.html b/templates/crate/source.html index 1dc2a62d5..7249b414c 100644 --- a/templates/crate/source.html +++ b/templates/crate/source.html @@ -29,13 +29,13 @@ {# If we are displaying a file, we also add a button to hide the file sidebar #} {% if has_file_content %}
  • - +
  • {% endif %} {# If this isn't the root folder, show a 'back' button #} {%- if show_parent_link -%}
  • - {{ "folder-open"|far(false, false, "") }} .. + {{ crate::f_a::icons::IconFolderOpen|far(false, false, "") }} ..
  • {%- endif -%} @@ -48,23 +48,23 @@ {# Directories #} {%- if file.mime == "dir" -%} - {{ "folder-open"|far(false, false, "") }} + {{ crate::f_a::icons::IconFolderOpen|far(false, false, "") }} {# Rust files #} {%- elif file.mime == "text/rust" -%} - {{ "rust"|fab(false, false, "") }} + {{ crate::f_a::icons::IconRust|fab(false, false, "") }} {# Cargo.lock #} {%- elif file.mime == "text/plain" && file.name == "Cargo.lock" -%} - {{ "lock"|fas(false, false, "") }} + {{ crate::f_a::icons::IconLock|fas(false, false, "") }} {# Markdown files #} {% elif file.mime == "text/markdown" %} - {{ "markdown"|fab(false, false, "") }} + {{ crate::f_a::icons::IconMarkdown|fab(false, false, "") }} {# .gitignore #} {% elif file.mime == "text/plain" && file.name == ".gitignore" %} - {{ "git-alt"|fab(false, false, "") }} + {{ crate::f_a::icons::IconGitAlt|fab(false, false, "") }} {# More ideas @@ -86,11 +86,11 @@ {# Text files or files which mime starts with `text` #} {%- elif file.mime == "text/plain" || file.mime|split_first("/") == Some("text") -%} - {{ "file-lines"|far(false, false, "") }} + {{ crate::f_a::icons::IconFileLines|far(false, false, "") }} {# Binary files and any unrecognized types #} {% else -%} - {{ "file"|far(false, false, "") }} + {{ crate::f_a::icons::IconFile|far(false, false, "") }} {%- endif -%} {{ file.name }} diff --git a/templates/header/global_alert.html b/templates/header/global_alert.html index 8bdb89f49..65743ef33 100644 --- a/templates/header/global_alert.html +++ b/templates/header/global_alert.html @@ -4,7 +4,7 @@ {%- if let Some(global_alert) = crate::GLOBAL_ALERT -%}
  • - {{- global_alert.fa_icon|fas(false, false, "") }} + {{- global_alert.fa_icon.clone()|fas(false, false, "") }} {{ global_alert.text -}}
  • diff --git a/templates/header/package_navigation.html b/templates/header/package_navigation.html index 1f6cb9a00..dbcd0f516 100644 --- a/templates/header/package_navigation.html +++ b/templates/header/package_navigation.html @@ -39,7 +39,7 @@

    {# The crate information tab #}
  • - {{ "cube"|fas(false, false, "") }} + {{ crate::f_a::icons::IconCube|fas(false, false, "") }} Crate
  • @@ -48,7 +48,7 @@

  • - {{ "folder-open"|far(false, false, "") }} + {{ crate::f_a::icons::IconFolderOpen|far(false, false, "") }} Source
  • @@ -57,7 +57,7 @@

  • - {{ "gears"|fas(false, false, "") }} + {{ crate::f_a::icons::IconGears|fas(false, false, "") }} Builds
  • @@ -66,7 +66,7 @@

  • - {{ "flag"|fas(false, false, "") }} + {{ crate::f_a::icons::IconFlag|fas(false, false, "") }} Feature flags
  • @@ -76,7 +76,7 @@

    {%- if metadata.rustdoc_status.unwrap_or_default() -%} - {{ "book"|fas(false, false, "") }} Documentation + {{ crate::f_a::icons::IconBook|fas(false, false, "") }} Documentation {%- endif -%}

    diff --git a/templates/header/topbar_begin.html b/templates/header/topbar_begin.html index 012073869..9e6005821 100644 --- a/templates/header/topbar_begin.html +++ b/templates/header/topbar_begin.html @@ -15,7 +15,7 @@ {# The top-left logo and name #} {# diff --git a/templates/header/topbar_end.html b/templates/header/topbar_end.html index 94dc7e4cc..8700b882b 100644 --- a/templates/header/topbar_end.html +++ b/templates/header/topbar_end.html @@ -60,7 +60,7 @@ {# The search bar #}
    {# If there is a search query, put it in the search bar #} diff --git a/templates/macros.html b/templates/macros.html index a8fa5deb1..c5ddd2c2d 100644 --- a/templates/macros.html +++ b/templates/macros.html @@ -143,10 +143,10 @@ {% if retain_fragment %}data-fragment="retain"{% endif %} > {% if warning %} - {{ "triangle-exclamation"|fas(false, false, "") }} + {{ crate::f_a::icons::IconTriangleExclamation|fas(false, false, "") }} {% endif %} {% if release.build_status == "in_progress" %} - {{ "gear"|fas(true, true, "") }} + {{ crate::f_a::icons::IconGear|fas(true, true, "") }} {% endif %} {{ release.version }} diff --git a/templates/releases/header.html b/templates/releases/header.html index a60011ef8..2ad3d7548 100644 --- a/templates/releases/header.html +++ b/templates/releases/header.html @@ -23,14 +23,14 @@

    {{ title }}

    • - {{ "leaf"|fas(false, false, "") }} + {{ crate::f_a::icons::IconLeaf|fas(false, false, "") }} Recent
    • - {{ "star"|fas(false, false, "") }} + {{ crate::f_a::icons::IconStar|fas(false, false, "") }} Stars
    • @@ -38,7 +38,7 @@

      {{ title }}

    • - {{ "triangle-exclamation"|fas(false, false, "") }} + {{ crate::f_a::icons::IconTriangleExclamation|fas(false, false, "") }} Recent Failures
    • @@ -46,7 +46,7 @@

      {{ title }}

    • - {{ "star"|far(false, false, "") }} + {{ crate::f_a::icons::IconStar|far(false, false, "") }} Failures By Stars
    • @@ -54,14 +54,14 @@

      {{ title }}

    • - {{ "chart-line"|fas(false, false, "") }} + {{ crate::f_a::icons::IconChartLine|fas(false, false, "") }} Activity
    • - {{ "list"|fas(false, false, "") }} + {{ crate::f_a::icons::IconList|fas(false, false, "") }} Queue
    • @@ -69,7 +69,7 @@

      {{ title }}

      {%- if !owner.is_empty() -%}
    • - {{ "user"|fas(false, false, "") }} + {{ crate::f_a::icons::IconUser|fas(false, false, "") }} {{ owner }}
    • diff --git a/templates/releases/releases.html b/templates/releases/releases.html index 7c4dd17f0..f02a1eab9 100644 --- a/templates/releases/releases.html +++ b/templates/releases/releases.html @@ -50,7 +50,7 @@ {{ release.name }}-{{ release.version }} {% if !has_unyanked_releases %} - {{ "trash"|fas(false, false, "") }} + {{ crate::f_a::icons::IconTrash|fas(false, false, "") }} Yanked {% endif %} @@ -64,7 +64,7 @@
      {{ release.stars }} - {{ "star"|fas(false, false, "") }} + {{ crate::f_a::icons::IconStar|fas(false, false, "") }}
      {%- elif let Some(build_time) = release.build_time -%}
      - {{ "arrow-left"|fas(false, false, "") }} Previous Page + {{ crate::f_a::icons::IconArrowLeft|fas(false, false, "") }} Previous Page {%- endif -%} {%- if show_next_page -%} - Next Page {{ "arrow-right"|fas(false, false, "") }} + Next Page {{ crate::f_a::icons::IconArrowRight|fas(false, false, "") }} {%- endif -%} {% endblock pagination %} diff --git a/templates/releases/search_results.html b/templates/releases/search_results.html index 07d4de83f..1ace55fe3 100644 --- a/templates/releases/search_results.html +++ b/templates/releases/search_results.html @@ -15,7 +15,7 @@
      Sort by {% set search_sort_by_val = search_sort_by.as_deref().unwrap_or_default() %}