1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! UUIDv1 Batteries
//!
//! This library provides convenient methods for generating version 1 UUIDs
//! and ordering them.

extern crate chrono;
#[macro_use]
extern crate lazy_static;
extern crate rand;
extern crate uuid;

use std::fmt;
use std::fs::File;
use std::io::Read;

use chrono::Utc;
use rand::Rand;
use uuid::{Uuid, UuidV1Context};

/// https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
#[derive(Debug)]
pub struct OrderedUuid(Uuid);

struct Config {
    ctx: UuidV1Context,
    node: Uuid,
}

lazy_static! {
    static ref CONFIG: Config = {
        let ctx = {
            let mut rng = rand::thread_rng();
            UuidV1Context::rand(&mut rng)
        };
        let node = {
            let mut machine_id = None;
            if let Ok(mut file) = File::open("/etc/machine-id") {
                let mut contents = String::new();
                if file.read_to_string(&mut contents).is_ok() {
                    if let Ok(id) = Uuid::parse_str(contents.trim()) {
                        machine_id = Some(id);
                    }
                }
            }
            match machine_id {
                Some(id) => id,
                None => Uuid::new_v4(),
            }
        };
        Config { ctx, node }
    };
}

pub trait Uuid1 {
    fn v1() -> Uuid;
    fn ordered(&self) -> Option<OrderedUuid>;
}

impl Uuid1 for Uuid {
    fn v1() -> Uuid {
        let ctx = &CONFIG.ctx;
        let node = &CONFIG.node.as_bytes()[..6];
        let now = Utc::now();
        let seconds = now.timestamp() as u64;
        let nsecs = now.timestamp_subsec_nanos();
        Uuid::new_v1(ctx, seconds, nsecs, node).unwrap()
    }

    fn ordered(&self) -> Option<OrderedUuid> {
        if self.get_version_num() == 1 {
            let ordered: [u8; 16] = {
                let bytes = self.as_bytes();
                unsafe {[
                    *bytes.get_unchecked(6),
                    *bytes.get_unchecked(7),
                    *bytes.get_unchecked(4),
                    *bytes.get_unchecked(5),
                    *bytes.get_unchecked(0),
                    *bytes.get_unchecked(1),
                    *bytes.get_unchecked(2),
                    *bytes.get_unchecked(3),
                    *bytes.get_unchecked(8),
                    *bytes.get_unchecked(9),
                    *bytes.get_unchecked(10),
                    *bytes.get_unchecked(11),
                    *bytes.get_unchecked(12),
                    *bytes.get_unchecked(13),
                    *bytes.get_unchecked(14),
                    *bytes.get_unchecked(15),
                ]}
            };
            let uuid = Uuid::from_bytes(&ordered).unwrap();
            return Some(OrderedUuid(uuid));
        }
        None
    }
}

impl fmt::Display for OrderedUuid {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[cfg(test)]
mod tests {
    use Uuid1;
    use uuid::Uuid;

    #[test]
    fn ordered_uuid_is_some() {
        let uuid = Uuid::v1();
        assert!(uuid.ordered().is_some());
    }
}