#[doc = include_str!("../README.md")]
#[macro_use]
extern crate lazy_static;
use data_encoding::Encoding;
use num::bigint::BigInt;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::time::{SystemTime, UNIX_EPOCH};
lazy_static! {
static ref B32: Encoding = {
let mut spec = data_encoding::Specification::new();
spec.symbols.push_str("234567abcdefghijklmnopqrstuvwxyz");
spec.encoding().unwrap()
};
}
#[non_exhaustive]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Id(String);
impl Deref for Id {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Ord for Id {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if self.len() == other.len() {
return self.0.cmp(&(other.0));
}
self.len().cmp(&(other.len()))
}
}
impl PartialOrd for Id {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Display for Id {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
pub fn lexicoid(timestamp: u64) -> Id {
let ts_bi = BigInt::from(timestamp);
Id(B32.encode(ts_bi.to_bytes_be().1.as_slice()))
}
pub fn lexicoid_now() -> Id {
let now = SystemTime::now();
let timestamp = now.duration_since(UNIX_EPOCH).unwrap().as_secs();
lexicoid(timestamp)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let cases = [
(0, "22"), (100, "gk"), (10000, "6wc2"), (500000, "2ykm2"), (1700000, "5bse2"), (28000000, "2apny22"), (550000000, "6567f22"), (1550000000, "flllz22"), (1654301676, "gehebv2"), (1654401676, "gei4p52"), (1674301676, "gj7x3v2"), (1674301677, "gj7x3vc"), ];
let mut ids: Vec<Id> = cases.iter().map(|(ts, _)| lexicoid(*ts)).collect();
ids.sort(); for (i, (ts, expected)) in cases.iter().enumerate() {
assert_eq!(ids[i].as_str(), *expected);
assert_eq!(ids[i].as_str(), lexicoid(*ts).as_str());
}
let id_now = lexicoid_now();
assert!(matches!(
id_now.cmp(&(ids[ids.len() - 1])),
std::cmp::Ordering::Greater
));
}
}