aid-rs 0.1.0

Misskey's aid/aidx implementation for Rust lang.
Documentation
use rand::Rng;
use chrono::{DateTime, TimeZone, Utc};
use std::num::ParseIntError;

pub fn get_noise() -> String {
    let mut rng = rand::thread_rng();
    let counter: u16 = rng.gen_range(0..=u16::MAX);
    format!("{:02x}", counter)
}

pub fn parse_aid(aid: &str) -> Result<DateTime<Utc>, Box<dyn std::error::Error>> {
    let base36_time = &aid[..8];
    let time_milliseconds = b36_decode(base36_time)?;
    let timestamp = 946684800 + time_milliseconds / 1000;
    Utc.timestamp_opt(timestamp as i64, 0)
        .single()
        .ok_or_else(|| "Invalid timestamp".into())
}

pub fn gen_aid() -> String {
    let current = ((chrono::Utc::now().timestamp() + 946684800) * 1000) as u64;
    let base36_time = b36_encode(current);
    let noise = get_noise();
    format!("{:0>8}{}", base36_time, noise)
}

pub fn gen_aidx() -> String {
    let current_time = ((chrono::Utc::now().timestamp() + 946684800) * 1000) as u64;
    let base36_time = b36_encode(current_time);
    let individual_id = format!("{:04X}", rand::thread_rng().gen_range(0..=u16::MAX));
    let counter = format!("{:04X}", rand::thread_rng().gen_range(0..=u16::MAX));
    format!("{:0>8}{}{}", base36_time, individual_id, counter)
}

pub fn parse_aidx(aidx: &str) -> Result<DateTime<Utc>, Box<dyn std::error::Error>> {
    let base36_time = &aidx[..8];
    let time_milliseconds = b36_decode(base36_time)?;
    let timestamp = 946684800 + time_milliseconds / 1000;
    Utc.timestamp_opt(timestamp as i64, 0)
        .single()
        .ok_or_else(|| "Invalid timestamp".into())
}

fn b36_encode(mut number: u64) -> String {
    let alphabet = "0123456789abcdefghijklmnopqrstuvwxyz";
    let mut base36 = String::new();
    while number > 0 {
        let (quotient, remainder) = (number / 36, number % 36);
        base36.insert(0, alphabet.chars().nth(remainder as usize).unwrap());
        number = quotient;
    }
    if base36.is_empty() {
        base36.push(alphabet.chars().nth(0).unwrap());
    }
    base36
}

fn b36_decode(number: &str) -> Result<u64, ParseIntError> {
    u64::from_str_radix(number, 36)
}