use rand::Rng;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::SystemTime;
use uuid::Uuid;
const DATA_CENTER_ID: u16 = 0;
const MACHINE_ID: u16 = 0;
const TIMESTAMP_LEFT_SHIFT: u64 = 22;
const DATA_CENTER_ID_SHIFT: u64 = 17;
const MACHINE_ID_SHIFT: u64 = 12;
const SEQUENCE_MASK: u64 = 0xfff;
static LAST_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
static SEQUENCE: AtomicU64 = AtomicU64::new(0);
pub struct IdUtil;
impl IdUtil {
pub fn gen_uuid() -> String {
Uuid::new_v4().to_string()
}
pub fn uuid32() -> String {
Uuid::new_v4().to_string().replace("-", "")
}
pub fn uuid36() -> String {
Uuid::new_v4().to_string()
}
pub fn random_string_id(length: usize) -> String {
let str_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let mut rng = rand::thread_rng();
let mut result = String::with_capacity(length);
for _ in 0..length {
let index = rng.gen_range(0..str_chars.len());
result.push(str_chars.chars().nth(index).unwrap());
}
result
}
pub fn tik_id() -> String {
format!(
"{}_{}_{}__",
Self::random_string_id(2),
Self::random_string_id(14),
Self::random_string_id(16)
)
}
pub fn snowflake_id() -> u64 {
let mut timestamp = Self::get_current_timestamp();
let last_timestamp = LAST_TIMESTAMP.load(Ordering::Relaxed);
if timestamp < last_timestamp {
panic!(
"Clock moved backwards. Refusing to generate id for {} milliseconds",
last_timestamp - timestamp
);
}
if timestamp == last_timestamp {
let sequence = SEQUENCE.fetch_add(1, Ordering::Relaxed) & SEQUENCE_MASK;
if sequence == 0 {
timestamp = Self::til_next_millis(last_timestamp);
}
} else {
SEQUENCE.store(0, Ordering::Relaxed);
}
LAST_TIMESTAMP.store(timestamp, Ordering::Relaxed);
(timestamp << TIMESTAMP_LEFT_SHIFT)
| ((DATA_CENTER_ID as u64) << DATA_CENTER_ID_SHIFT)
| ((MACHINE_ID as u64) << MACHINE_ID_SHIFT)
| SEQUENCE.load(Ordering::Relaxed)
}
fn get_current_timestamp() -> u64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Time went backwards")
.as_millis() as u64
}
fn til_next_millis(last_timestamp: u64) -> u64 {
let mut timestamp = Self::get_current_timestamp();
while timestamp <= last_timestamp {
timestamp = Self::get_current_timestamp();
}
timestamp
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_uuid() {
let uuid = IdUtil::uuid32();
println!("{}", uuid);
let uuid = IdUtil::uuid36();
println!("{}", uuid);
}
#[test]
fn test_random_string_id() {
let id = IdUtil::random_string_id(128);
println!("{}", id)
}
#[test]
fn test_snowflake_id() {
let id = IdUtil::snowflake_id();
println!("{}", id);
println!("{:b}", id);
}
}