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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use {
    chrono::{DateTime, TimeZone, Utc},
    rand::random,
    serde::{Deserialize, Deserializer, Serialize, Serializer},
    std::{
        fmt::{Display, Formatter, Result as FmtResult},
        str::FromStr,
        time::SystemTime,
    },
    uuid::Uuid,
};

/// AWS request id implementation.
///
/// This implementation abuses the UUID format the embed a timestamp in the UUID to make it easier to track down the
/// request in the logs. This timestamp has a resolution of 1s and is based on the system clock, so it's not guaranteed
/// to be unique; thus, a random number is also embedded in the UUID.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RequestId {
    id: Uuid,
}

impl RequestId {
    /// Create a new request id from the current system time and a random number.
    pub fn new() -> Self {
        let now = SystemTime::now();
        let offset = match now.duration_since(SystemTime::UNIX_EPOCH) {
            Ok(offset) => offset,
            Err(_) => SystemTime::UNIX_EPOCH
                .duration_since(now)
                .expect("SystemTime cannot be represented as a duration since the Unix epoch"),
        };

        let timestamp = offset.as_secs();
        Self::from_timestamp(timestamp as i64)
    }

    /// Create a new request id from the given timestamp, in seconds from the Unix epoch (January 1, 1970 at
    /// 00:00:00 UTC) and random number.
    pub fn from_timestamp_and_random(unix_timestamp: i64, random: u64) -> Self {
        let mut bytes = [0u8; 16];
        bytes[0..8].copy_from_slice(&unix_timestamp.to_be_bytes());
        bytes[8..16].copy_from_slice(&random.to_be_bytes());

        Self {
            id: Uuid::from_bytes(bytes),
        }
    }

    /// Create a new request id from the given timestamp and random number.
    pub fn from_datetime_and_random<Tz: TimeZone>(datetime: DateTime<Tz>, random: u64) -> Self {
        let unix_timestamp = datetime.timestamp();
        Self::from_timestamp_and_random(unix_timestamp, random)
    }

    /// Create a new request id from the given timestamp, in seconds from the Unix epoch (January 1, 1970 at
    /// 00:00:00 UTC).
    pub fn from_timestamp(unix_timestamp: i64) -> Self {
        let random: u64 = random();
        Self::from_timestamp_and_random(unix_timestamp, random)
    }

    /// Create a new request id from the given timestamp.
    pub fn from_datetime<Tz: TimeZone>(datetime: DateTime<Tz>) -> Self {
        let unix_timestamp = datetime.timestamp();
        Self::from_timestamp(unix_timestamp)
    }

    /// Returns the Unix timestamp, in seconds from the Unix epoch (January 1, 1970 at 00:00:00 UTC), embedded in
    /// this request id.
    #[inline]
    pub fn unix_timestamp(&self) -> u64 {
        u64::from_be_bytes(self.id.as_bytes()[0..8].try_into().unwrap())
    }

    /// Returns the timestamp embedded in this request id.
    #[inline]
    pub fn datetime(&self) -> DateTime<Utc> {
        Utc.timestamp_opt(self.unix_timestamp() as i64, 0).unwrap()
    }

    /// Returns this request id as a UUID.
    #[inline]
    pub fn uuid(&self) -> Uuid {
        self.id
    }
}

impl Default for RequestId {
    fn default() -> Self {
        Self::new()
    }
}

impl Display for RequestId {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.id)
    }
}

impl<'de> Deserialize<'de> for RequestId {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        Ok(RequestId {
            id: Uuid::deserialize(deserializer)?,
        })
    }
}

impl FromStr for RequestId {
    type Err = uuid::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(RequestId {
            id: Uuid::parse_str(s)?,
        })
    }
}

impl Serialize for RequestId {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        serializer.serialize_str(&self.id.to_string())
    }
}