|
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,177 @@ 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 = 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)?; |
| 252 | + |
| 253 | + code_output.push('}'); |
| 254 | + std::fs::write(out_dir.join("icons.rs"), code_output)?; |
| 255 | + Ok(()) |
| 256 | +} |
| 257 | + |
88 | 258 | fn write_git_version(out_dir: &Path) -> Result<()> { |
89 | 259 | let maybe_hash = get_git_hash()?; |
90 | 260 | let git_hash = maybe_hash.as_deref().unwrap_or("???????"); |
|
0 commit comments