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