Skip to content

Commit

Permalink
use quote to generate font-awesome code
Browse files Browse the repository at this point in the history
  • Loading branch information
syphar committed Sep 29, 2024
1 parent 41d5e31 commit a33f0ee
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 49 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/font-awesome-as-a-crate/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ license = "CC-BY-4.0 AND MIT"
description = "Font Awesome Free, packaged as a crate"
repository = "https://github.com/rust-lang/docs.rs"

[build-dependencies]
quote = "1.0.37"
proc-macro2 = "1.0.86"
syn = "2.0.77"
prettyplease = "0.2.22"
114 changes: 65 additions & 49 deletions crates/font-awesome-as-a-crate/build.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use std::{
collections::HashMap,
env,
fmt::Write as FmtWrite,
fs::{read_dir, File},
io::{Read, Write},
path::Path,
};
use proc_macro2::Literal;
use quote::{format_ident, quote};
use std::{collections::HashMap, env, fs, path::Path};

fn main() {
println!("cargo::rustc-check-cfg=cfg(font_awesome_out_dir)");
Expand All @@ -22,70 +17,91 @@ fn capitalize_first_letter(s: &str) -> String {
}

fn write_fontawesome_sprite() {
let mut match_arms = vec![];
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, 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();
let dir =
fs::read_dir(Path::new("fontawesome-free-6.2.0-desktop/svgs").join(dirname)).unwrap();

for file in dir {
let file = file.expect("fontawesome directory access");
let data = fs::read_to_string(file.path()).expect("fontawesome file read");

let filename = file
.file_name()
.into_string()
.expect("fontawesome filenames are unicode");
let mut file = File::open(file.path()).expect("fontawesome file access");
data.clear();
file.read_to_string(&mut data)
.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}"#,"####).as_bytes(),
)
.expect("write fontawesome file");
.expect("fontawesome filenames are unicode")
.replace(".svg", "");

let dirname_literal = Literal::byte_string(dirname.as_bytes());
let filename_literal = Literal::byte_string(filename.as_bytes());
match_arms.push(quote! {
(#dirname_literal, #filename_literal) => #data,
});

types
.entry(filename)
.or_insert_with(|| (data.clone(), Vec::with_capacity(3)))
.1
.push(trait_name);
}
}
dest_file
.write_all(b"_=>\"\"}} pub mod icons { use super::{IconStr, Regular, Brands, Solid};")
.expect("fontawesome fn write");

let mut types_output = vec![];

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
let type_name = format_ident!("{}", type_name);

let kind_impls: Vec<_> = kinds
.iter()
.map(|k| {
let k = format_ident!("{}", k);
quote! {
impl #k for #type_name {}
}
})
.collect();

types_output.push(quote! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct #type_name;

impl IconStr for #type_name {
fn icon_name(&self) -> &'static str { #icon }
fn icon_str(&self) -> &'static str { #data }
}

#(#kind_impls)*
});
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");
let token_stream = quote! {
const fn fontawesome_svg(dir: &str, file: &str) -> &'static str {
// we are using byte literals to match because they can be evaluated in a
// `const` context, and `str` cannot.
match(dir.as_bytes(), file.as_bytes()) {
#(#match_arms)*
_=> ""
}
}

pub mod icons {
use super::{IconStr, Regular, Brands, Solid};

#(#types_output)*
}
};

let dest_path = Path::new(&env::var("OUT_DIR").unwrap()).join("fontawesome.rs");

let output = prettyplease::unparse(&syn::parse2(token_stream).unwrap());

fs::write(&dest_path, output.as_bytes()).expect("fontawesome output write");
}

0 comments on commit a33f0ee

Please sign in to comment.