1use std::fmt;
2
3use crate::{Decode, Encode, EncodedSize, Result};
4
5#[derive(Clone, Copy, Default, PartialEq, Eq, Hash)]
16pub struct Uuid {
17 pub most: u64,
19 pub least: u64,
21}
22
23impl Uuid {
24 pub fn new(most: u64, least: u64) -> Self {
26 Self { most, least }
27 }
28
29 pub fn from_bytes(bytes: [u8; 16]) -> Self {
34 let most = u64::from_be_bytes(bytes[..8].try_into().unwrap());
35 let least = u64::from_be_bytes(bytes[8..].try_into().unwrap());
36 Self { most, least }
37 }
38
39 pub fn to_bytes(self) -> [u8; 16] {
41 let mut bytes = [0u8; 16];
42 bytes[..8].copy_from_slice(&self.most.to_be_bytes());
43 bytes[8..].copy_from_slice(&self.least.to_be_bytes());
44 bytes
45 }
46}
47
48impl Encode for Uuid {
54 fn encode(&self, buf: &mut Vec<u8>) -> Result<()> {
56 self.most.encode(buf)?;
57 self.least.encode(buf)
58 }
59}
60
61impl Decode for Uuid {
66 fn decode(buf: &mut &[u8]) -> Result<Self> {
70 let most = u64::decode(buf)?;
71 let least = u64::decode(buf)?;
72 Ok(Self { most, least })
73 }
74}
75
76impl EncodedSize for Uuid {
78 fn encoded_size(&self) -> usize {
79 16
80 }
81}
82
83impl fmt::Display for Uuid {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 let bytes = self.to_bytes();
90 write!(
91 f,
92 "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
93 bytes[0],
94 bytes[1],
95 bytes[2],
96 bytes[3],
97 bytes[4],
98 bytes[5],
99 bytes[6],
100 bytes[7],
101 bytes[8],
102 bytes[9],
103 bytes[10],
104 bytes[11],
105 bytes[12],
106 bytes[13],
107 bytes[14],
108 bytes[15],
109 )
110 }
111}
112
113impl fmt::Debug for Uuid {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(f, "Uuid({})", self)
117 }
118}
119
120impl From<[u8; 16]> for Uuid {
122 fn from(bytes: [u8; 16]) -> Self {
123 Self::from_bytes(bytes)
124 }
125}
126
127impl From<Uuid> for [u8; 16] {
129 fn from(uuid: Uuid) -> Self {
130 uuid.to_bytes()
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 fn roundtrip(most: u64, least: u64) {
139 let uuid = Uuid::new(most, least);
140 let mut buf = Vec::with_capacity(uuid.encoded_size());
141 uuid.encode(&mut buf).unwrap();
142 assert_eq!(buf.len(), 16);
143
144 let mut cursor = buf.as_slice();
145 let decoded = Uuid::decode(&mut cursor).unwrap();
146 assert!(cursor.is_empty());
147 assert_eq!(decoded, uuid);
148 }
149
150 #[test]
151 fn zero_uuid() {
152 roundtrip(0, 0);
153 }
154
155 #[test]
156 fn max_uuid() {
157 roundtrip(u64::MAX, u64::MAX);
158 }
159
160 #[test]
161 fn known_uuid() {
162 let uuid = Uuid::new(0x069a79f444e94726, 0xa5befca90e38aaf5);
164 roundtrip(uuid.most, uuid.least);
165 }
166
167 #[test]
168 fn display_format() {
169 let uuid = Uuid::new(0x550e8400e29b41d4, 0xa716446655440000);
170 assert_eq!(uuid.to_string(), "550e8400-e29b-41d4-a716-446655440000");
171 }
172
173 #[test]
174 fn debug_format() {
175 let uuid = Uuid::new(0x550e8400e29b41d4, 0xa716446655440000);
176 assert_eq!(
177 format!("{:?}", uuid),
178 "Uuid(550e8400-e29b-41d4-a716-446655440000)"
179 );
180 }
181
182 #[test]
183 fn from_bytes() {
184 let bytes = [
185 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
186 0x00, 0x00,
187 ];
188 let uuid = Uuid::from_bytes(bytes);
189 assert_eq!(uuid.most, 0x550e8400e29b41d4);
190 assert_eq!(uuid.least, 0xa716446655440000);
191 }
192
193 #[test]
194 fn to_bytes() {
195 let uuid = Uuid::new(0x550e8400e29b41d4, 0xa716446655440000);
196 let bytes = uuid.to_bytes();
197 assert_eq!(
198 bytes,
199 [
200 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
201 0x00, 0x00
202 ]
203 );
204 }
205
206 #[test]
207 fn bytes_roundtrip() {
208 let original = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
209 let uuid = Uuid::from(original);
210 let back: [u8; 16] = uuid.into();
211 assert_eq!(back, original);
212 }
213
214 #[test]
215 fn encoded_size_is_16() {
216 assert_eq!(Uuid::new(0, 0).encoded_size(), 16);
217 }
218
219 #[test]
220 fn underflow() {
221 let mut cursor: &[u8] = &[0x01; 15];
222 assert!(Uuid::decode(&mut cursor).is_err());
223 }
224
225 mod proptests {
226 use super::*;
227 use proptest::prelude::*;
228
229 proptest! {
230 #[test]
231 fn uuid_roundtrip(most: u64, least: u64) {
232 roundtrip(most, least);
233 }
234 }
235 }
236}