1use std::{io::Write as _, path::PathBuf};
17
18use solium_animation::Curve;
19
20fn main() {
21 let mut args = std::env::args_os().skip(1).map(PathBuf::from);
22 let (Some(animation), Some(layout)) = (args.next(), args.next()) else {
23 eprintln!("usage: preview <animation.wasm> <layout.wasm>");
24 eprintln!(" run dev/preview instead, which builds them first");
25 std::process::exit(2);
26 };
27
28 let read = |path: &PathBuf| match std::fs::read(path) {
29 Ok(bytes) => bytes,
30 Err(err) => {
31 eprintln!("error: could not read {}: {err}", path.display());
32 std::process::exit(1);
33 }
34 };
35 let bytes = read(&animation);
36 let layout_bytes = read(&layout);
37
38 let names: Vec<String> = Curve::all()
39 .into_iter()
40 .map(|(name, _)| format!("{name:?}"))
41 .collect();
42
43 const PAGE: &str = include_str!("../../preview/index.html");
44 let page = PAGE
45 .replace("/*CURVES*/ null", &format!("[{}]", names.join(", ")))
46 .replace("/*WASM*/ \"\"", &format!("{:?}", base64(&bytes)))
47 .replace("/*LAYOUT*/ \"\"", &format!("{:?}", base64(&layout_bytes)));
48
49 let mut stdout = std::io::stdout().lock();
50 if let Err(err) = stdout.write_all(page.as_bytes()) {
51 eprintln!("error: could not write the page: {err}");
52 std::process::exit(1);
53 }
54}
55
56fn base64(bytes: &[u8]) -> String {
61 const ALPHABET: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
62
63 let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4);
64 for chunk in bytes.chunks(3) {
65 let mut block = [0_u8; 3];
66 block[..chunk.len()].copy_from_slice(chunk);
67 let packed = (u32::from(block[0]) << 16) | (u32::from(block[1]) << 8) | u32::from(block[2]);
68
69 for slot in 0..4 {
70 if slot <= chunk.len() {
71 let index = ((packed >> (18 - slot * 6)) & 0x3F) as usize;
72 out.push(char::from(ALPHABET[index]));
73 } else {
74 out.push('=');
75 }
76 }
77 }
78 out
79}
80
81#[cfg(test)]
82mod tests {
83 use super::base64;
84
85 #[test]
86 fn base64_matches_the_standard_including_padding() {
87 assert_eq!(base64(b""), "");
90 assert_eq!(base64(b"f"), "Zg==");
91 assert_eq!(base64(b"fo"), "Zm8=");
92 assert_eq!(base64(b"foo"), "Zm9v");
93 assert_eq!(base64(b"foob"), "Zm9vYg==");
94 assert_eq!(base64(b"fooba"), "Zm9vYmE=");
95 assert_eq!(base64(b"foobar"), "Zm9vYmFy");
96 }
97
98 #[test]
99 fn high_bytes_survive() {
100 assert_eq!(base64(&[0x00, 0x10, 0x83, 0xFF]), "ABCD/w==");
101 }
102}