monetize_estate_side/
lib.rs1#[cfg(feature = "pump")]
23pub mod pump;
24pub mod sign_in;
25#[cfg(feature = "sold-door")]
26pub mod sold_door;
27
28pub fn now_unix_ms() -> u64 {
30 std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0)
31}
32
33pub fn hex(bytes: &[u8]) -> String {
35 bytes.iter().map(|b| format!("{b:02x}")).collect()
36}
37
38pub fn key32(text: &str) -> Option<[u8; 32]> {
40 let t = text.trim();
41 if t.len() == 64 {
42 return unhex32(t, "the key").ok();
43 }
44 monetize_embed::signing::base64_decode(t)?.try_into().ok()
45}
46
47pub fn unhex32(s: &str, what: &str) -> Result<[u8; 32], String> {
49 let s = s.trim();
50 if s.len() != 64 || !s.is_ascii() {
51 return Err(format!("{what} is not 64 hex"));
52 }
53 let v: Result<Vec<u8>, _> = (0..64).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16)).collect();
54 v.map_err(|_| format!("{what} is not 64 hex"))?.try_into().map_err(|_| format!("{what} is not 32 bytes"))
55}
56
57#[cfg(feature = "pump")]
59pub(crate) fn write_atomic(path: &std::path::Path, bytes: &[u8], secret: bool) -> std::io::Result<()> {
60 use std::io::Write;
61 let dir = path.parent().unwrap_or(std::path::Path::new("."));
62 std::fs::create_dir_all(dir)?;
63 let tmp = path.with_extension("tmp");
64 let mut opts = std::fs::OpenOptions::new();
65 opts.write(true).create(true).truncate(true);
66 #[cfg(unix)]
67 if secret {
68 use std::os::unix::fs::OpenOptionsExt;
69 opts.mode(0o600);
70 }
71 #[cfg(not(unix))]
72 let _ = secret;
73 let mut f = opts.open(&tmp)?;
74 f.write_all(bytes)?;
75 f.sync_all()?;
76 std::fs::rename(&tmp, path)
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn a_key_is_64_hex_or_44_base64_and_anything_else_is_none() {
85 let k = [0x11u8; 32];
86 assert_eq!(key32(&hex(&k)), Some(k));
87 assert_eq!(key32(&format!(" {}\n", hex(&k))), Some(k), "trimmed");
88 assert_eq!(key32(&monetize_embed::signing::base64_encode(&k)), Some(k));
89 assert_eq!(key32("11"), None);
90 assert_eq!(key32(&"zz".repeat(32)), None);
91 assert!(unhex32(&"1".repeat(63), "the client key").unwrap_err().contains("the client key is not 64 hex"));
92 assert!(unhex32(&"é".repeat(32), "k").is_err(), "a non-ASCII text never panics on a char boundary");
93 assert!(now_unix_ms() > 1_700_000_000_000);
94 }
95}