Generate a fresh lowercased ULID-style stem: a 48-bit millisecond timestamp followed by 80 bits of randomness, encoded in Crockford base32 (26 chars), lowercased. No ULID crate is a workspace dependency, so this is a self-contained generator using the existing `chrono` + `uuid` deps — deliberately NOT a `PREFIX-N` scheme (memories have no prefix allocator).
()
| 201 | /// self-contained generator using the existing `chrono` + `uuid` deps — |
| 202 | /// deliberately NOT a `PREFIX-N` scheme (memories have no prefix allocator). |
| 203 | fn generate_ulid_lowercased() -> String { |
| 204 | const CROCKFORD: &[u8; 32] = b"0123456789abcdefghjkmnpqrstvwxyz"; |
| 205 | |
| 206 | let now_ms = chrono::Utc::now().timestamp_millis().max(0) as u128; |
| 207 | // 80 bits of randomness from a v4 UUID. |
| 208 | let rand = uuid::Uuid::new_v4().as_u128(); |
| 209 | |
| 210 | // A ULID is a 128-bit value: 48-bit time (high) + 80-bit randomness (low). |
| 211 | let value: u128 = (now_ms << 80) | (rand & ((1u128 << 80) - 1)); |
| 212 | |
| 213 | // Encode the 128-bit value as 26 Crockford base32 chars (130 bits, so the |
| 214 | // top 2 bits of the first symbol are always 0 — standard ULID layout). |
| 215 | let mut out = [0u8; 26]; |
| 216 | let mut v = value; |
| 217 | for slot in out.iter_mut().rev() { |
| 218 | *slot = CROCKFORD[(v & 0x1f) as usize]; |
| 219 | v >>= 5; |
| 220 | } |
| 221 | String::from_utf8(out.to_vec()).expect("crockford alphabet is valid ASCII") |
| 222 | } |
| 223 | |
| 224 | #[cfg(test)] |
| 225 | mod tests { |