//! Build-time generation of Memospot-specific shortcut artifacts. use std::{fs, path::Path}; /// Data describing one shortcut for the native menu and JavaScript polyfill. pub struct ShortcutBinding { name: &'static str, command: &'static str, accelerator: &'static str, codes: &'static [&'static str], } impl ShortcutBinding { /// Create a binding from its native menu and frontend shortcut metadata. pub const fn new( name: &'static str, command: &'static str, accelerator: &'static str, codes: &'static [&'static str], ) -> Self { Self { name, command, accelerator, codes, } } } /// Write the JavaScript shortcut polyfill and Rust accelerator constants to `out_dir`. /// /// The caller supplies bindings expanded from /// `crates/memospot/src/shortcut_bindings.in.rs`, which remains the source of truth. pub fn generate_shortcut_artifacts(out_dir: &Path, bindings: &[ShortcutBinding]) { let mut conditions = format!("{:>4}switch (e.code) {{\n", ""); for binding in bindings { for code in binding.codes { let cmd = binding.command; conditions.push_str(&format!( " case \"{code}\":\n cmd = \"{cmd}\";\n break;\n" )); } } conditions.push_str(" default: break;\n }"); let debug = if cfg!(debug_assertions) { "console.warn('Memospot shortcut polyfill loaded successfully.');" } else { "" }; let js = format!( r#" /* * Shortcut polyfill. * * Auto-generated by build.rs from src/shortcut_bindings.in.rs. */ document.addEventListener("keydown", (e) => {{ if (!(e.ctrlKey || e.metaKey)) return; let cmd = null; {conditions} if (!cmd) return; e.preventDefault(); void window.__TAURI__.event.emit("memospot-shortcut", cmd).catch((error) => {{ console.error(`Memospot shortcut "${{cmd}}" failed:`, error); }}); }}); {debug} "# ); let mut accel = String::new(); for binding in bindings { accel.push_str(&format!( "#[allow(dead_code)]\npub const {}: &str = \"{}\";\n", binding.name, binding.accelerator )); } fs::write(out_dir.join("shortcut_polyfill.js"), js) .expect("failed to write generated shortcut polyfill"); fs::write(out_dir.join("shortcut_accelerators.rs"), accel) .expect("failed to write generated shortcut accelerators"); println!("cargo:rerun-if-changed=src/shortcut_bindings.in.rs"); } #[cfg(test)] mod tests { use std::fs; use super::*; #[test] fn generated_polyfill_handles_numpad_commands_without_unhandled_rejections() { let out_dir = tempfile::tempdir().unwrap(); let bindings = [ShortcutBinding::new( "ZOOM_OUT", "zoom_out", "CmdOrCtrl+-", &["NumpadSubtract"], )]; generate_shortcut_artifacts(out_dir.path(), &bindings); let polyfill = fs::read_to_string(out_dir.path().join("shortcut_polyfill.js")).unwrap(); assert!(polyfill.contains("case \"NumpadSubtract\":")); assert!( polyfill.contains("window.__TAURI__.event.emit(\"memospot-shortcut\", cmd).catch") ); } }