use crate::errors::{ValidationError, ValidationResult};
use chrono::offset::Utc;
use chrono::DateTime;
use rand::{OsRng, Rng};
use std::env;
use uuid::v1::Context;
use uuid::Uuid;
const TEMP_PATH_RANDOM_PART_LENGTH: usize = 8;
const NODE_ID: [u8; 6] = [0, 0, 0, 0, 0, 0];
lazy_static! {
static ref CONTEXT: Context = Context::new(0);
}
pub fn generate_temporary_path() -> String {
let mut path = env::temp_dir();
path.push(generate_random_secret(TEMP_PATH_RANDOM_PART_LENGTH));
path.to_str()
.expect("Expected to be able to parse the temp path into UTF-8")
.to_string()
}
pub fn generate_uuid_v1() -> Uuid {
let now = Utc::now();
Uuid::new_v1(
&*CONTEXT,
now.timestamp() as u64,
now.timestamp_subsec_nanos(),
&NODE_ID,
)
.expect("Expected to be able to generate a UUID")
}
pub fn generate_random_secret(count: usize) -> String {
let mut chars = vec![];
let options = b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
let mut rng = OsRng::new().unwrap();
for _ in 0..count {
let c: u8 = *rng.choose(options).unwrap();
chars.push(c);
}
String::from_utf8(chars).unwrap()
}
pub fn next_uuid(uuid: Uuid) -> ValidationResult<Uuid> {
let mut bytes = *uuid.as_bytes();
for i in (0..16).rev() {
if bytes[i] < 255 {
bytes[i] += 1;
return Ok(Uuid::from_slice(&bytes[..]).unwrap());
} else {
bytes[i] = 0;
}
}
Err(ValidationError::CannotIncrementUuid)
}
pub fn nanos_since_epoch(datetime: &DateTime<Utc>) -> u64 {
let timestamp = datetime.timestamp() as u64;
let nanoseconds = u64::from(datetime.timestamp_subsec_nanos());
timestamp * 1_000_000_000 + nanoseconds
}
#[cfg(test)]
mod tests {
use super::{generate_random_secret, generate_temporary_path, generate_uuid_v1, nanos_since_epoch, next_uuid};
use chrono::{DateTime, NaiveDateTime, Utc};
use core::str::FromStr;
use regex::Regex;
use uuid::Uuid;
#[test]
fn should_generate_temporary_path() {
let first = generate_temporary_path();
let second = generate_temporary_path();
assert!(first.len() > 0);
assert!(second.len() > 0);
assert_ne!(first, second);
}
#[test]
fn should_generate_new_uuid_v1() {
let first = generate_uuid_v1();
let second = generate_uuid_v1();
assert_ne!(first, second);
}
#[test]
fn should_generate_random_secret() {
let secret = generate_random_secret(8);
assert!(Regex::new(r"[a-zA-Z0-9]{8}").unwrap().is_match(&secret[..]));
}
#[test]
fn should_generate_next_uuid() {
let result = next_uuid(Uuid::from_str("16151dea-a538-4bf1-9559-851e256cf139").unwrap());
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
Uuid::from_str("16151dea-a538-4bf1-9559-851e256cf13a").unwrap()
);
let from_uuid = Uuid::from_str("ffffffff-ffff-ffff-ffff-ffffffffffff").unwrap();
assert!(next_uuid(from_uuid).is_err());
}
#[test]
fn should_generate_nanos_since_epoch() {
let datetime = DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(61, 62), Utc);
assert_eq!(nanos_since_epoch(&datetime), 61000000062);
}
}