| 
1 | 1 | 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 | +};  | 
3 | 7 | 
 
  | 
4 | 8 | mod tracked {  | 
5 | 9 |     use once_cell::sync::Lazy;  | 
@@ -80,11 +84,178 @@ fn main() -> Result<()> {  | 
80 | 84 |     write_known_targets(out_dir)?;  | 
81 | 85 |     compile_syntax(out_dir).context("could not compile syntax files")?;  | 
82 | 86 | 
 
  | 
 | 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 | + | 
83 | 94 |     // trigger recompilation when a new migration is added  | 
84 | 95 |     println!("cargo:rerun-if-changed=migrations");  | 
85 | 96 |     Ok(())  | 
86 | 97 | }  | 
87 | 98 | 
 
  | 
 | 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 = format!(  | 
 | 140 | +        r#"pub(crate) mod icons {{  | 
 | 141 | +    fn render(  | 
 | 142 | +        css_class: &str,  | 
 | 143 | +        fw: bool,  | 
 | 144 | +        spin: bool,  | 
 | 145 | +        extra: &str,  | 
 | 146 | +    ) -> rinja::filters::Safe<String> {{  | 
 | 147 | +        let mut classes = vec!["fa-svg"];  | 
 | 148 | +        if fw {{  | 
 | 149 | +            classes.push("fa-svg-fw");  | 
 | 150 | +        }}  | 
 | 151 | +        if spin {{  | 
 | 152 | +            classes.push("fa-svg-spin");  | 
 | 153 | +        }}  | 
 | 154 | +        if !extra.is_empty() {{  | 
 | 155 | +            classes.push(extra);  | 
 | 156 | +        }}  | 
 | 157 | +        let icon = format!(  | 
 | 158 | +            "<span class=\"{{css_class}} {{class}}\" aria-hidden=\"true\"></span>",  | 
 | 159 | +            class = classes.join(" "),  | 
 | 160 | +        );  | 
 | 161 | +
  | 
 | 162 | +        rinja::filters::Safe(icon)  | 
 | 163 | +    }}"#  | 
 | 164 | +    );  | 
 | 165 | +    let mut css_output = r#".svg-clipboard {  | 
 | 166 | +    /* This icon is copied from crates.io */  | 
 | 167 | +    --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>');  | 
 | 168 | +    -webkit-mask: var(--svg-clipboard) no-repeat center;  | 
 | 169 | +    mask: var(--svg-clipboard) no-repeat center;  | 
 | 170 | +}"#.to_string();  | 
 | 171 | + | 
 | 172 | +    let brands: &[&dyn f_a::Brands] = &[  | 
 | 173 | +        &f_a::icons::IconFonticons,  | 
 | 174 | +        &f_a::icons::IconRust,  | 
 | 175 | +        &f_a::icons::IconMarkdown,  | 
 | 176 | +        &f_a::icons::IconGitAlt,  | 
 | 177 | +    ];  | 
 | 178 | +    let regular: &[&dyn f_a::Regular] = &[  | 
 | 179 | +        &f_a::icons::IconFileLines,  | 
 | 180 | +        &f_a::icons::IconFolderOpen,  | 
 | 181 | +        &f_a::icons::IconFile,  | 
 | 182 | +        &f_a::icons::IconStar,  | 
 | 183 | +    ];  | 
 | 184 | +    let solid: &[&dyn f_a::Solid] = &[  | 
 | 185 | +        &f_a::icons::IconCircleInfo,  | 
 | 186 | +        &f_a::icons::IconGears,  | 
 | 187 | +        &f_a::icons::IconTable,  | 
 | 188 | +        &f_a::icons::IconRoad,  | 
 | 189 | +        &f_a::icons::IconDownload,  | 
 | 190 | +        &f_a::icons::IconCubes,  | 
 | 191 | +        &f_a::icons::IconSquareRss,  | 
 | 192 | +        &f_a::icons::IconFileLines,  | 
 | 193 | +        &f_a::icons::IconCheck,  | 
 | 194 | +        &f_a::icons::IconTriangleExclamation,  | 
 | 195 | +        &f_a::icons::IconGear,  | 
 | 196 | +        &f_a::icons::IconX,  | 
 | 197 | +        &f_a::icons::IconHouse,  | 
 | 198 | +        &f_a::icons::IconCodeBranch,  | 
 | 199 | +        &f_a::icons::IconStar,  | 
 | 200 | +        &f_a::icons::IconCircleExclamation,  | 
 | 201 | +        &f_a::icons::IconCube,  | 
 | 202 | +        &f_a::icons::IconChevronLeft,  | 
 | 203 | +        &f_a::icons::IconChevronRight,  | 
 | 204 | +        &f_a::icons::IconFolderOpen,  | 
 | 205 | +        &f_a::icons::IconLock,  | 
 | 206 | +        &f_a::icons::IconFlag,  | 
 | 207 | +        &f_a::icons::IconBook,  | 
 | 208 | +        &f_a::icons::IconMagnifyingGlass,  | 
 | 209 | +        &f_a::icons::IconLeaf,  | 
 | 210 | +        &f_a::icons::IconChartLine,  | 
 | 211 | +        &f_a::icons::IconList,  | 
 | 212 | +        &f_a::icons::IconUser,  | 
 | 213 | +        &f_a::icons::IconTrash,  | 
 | 214 | +        &f_a::icons::IconArrowLeft,  | 
 | 215 | +        &f_a::icons::IconArrowRight,  | 
 | 216 | +        &f_a::icons::IconLink,  | 
 | 217 | +        &f_a::icons::IconScaleUnbalancedFlip,  | 
 | 218 | +        &f_a::icons::IconSpinner,  | 
 | 219 | +    ];  | 
 | 220 | + | 
 | 221 | +    for icon in brands {  | 
 | 222 | +        render_icon(  | 
 | 223 | +            icon.icon_name(),  | 
 | 224 | +            icon.icon_str(),  | 
 | 225 | +            format!("{icon:?}"),  | 
 | 226 | +            &mut code_output,  | 
 | 227 | +            &mut css_output,  | 
 | 228 | +            "brands",  | 
 | 229 | +        );  | 
 | 230 | +    }  | 
 | 231 | +    for icon in regular {  | 
 | 232 | +        render_icon(  | 
 | 233 | +            icon.icon_name(),  | 
 | 234 | +            icon.icon_str(),  | 
 | 235 | +            format!("{icon:?}"),  | 
 | 236 | +            &mut code_output,  | 
 | 237 | +            &mut css_output,  | 
 | 238 | +            "regular",  | 
 | 239 | +        );  | 
 | 240 | +    }  | 
 | 241 | +    for icon in solid {  | 
 | 242 | +        render_icon(  | 
 | 243 | +            icon.icon_name(),  | 
 | 244 | +            icon.icon_str(),  | 
 | 245 | +            format!("{icon:?}"),  | 
 | 246 | +            &mut code_output,  | 
 | 247 | +            &mut css_output,  | 
 | 248 | +            "solid",  | 
 | 249 | +        );  | 
 | 250 | +    }  | 
 | 251 | + | 
 | 252 | +    std::fs::write(css_path, css_output)?;  | 
 | 253 | + | 
 | 254 | +    code_output.push('}');  | 
 | 255 | +    std::fs::write(out_dir.join("icons.rs"), code_output)?;  | 
 | 256 | +    Ok(())  | 
 | 257 | +}  | 
 | 258 | + | 
88 | 259 | fn write_git_version(out_dir: &Path) -> Result<()> {  | 
89 | 260 |     let maybe_hash = get_git_hash()?;  | 
90 | 261 |     let git_hash = maybe_hash.as_deref().unwrap_or("???????");  | 
 | 
0 commit comments