1
1
use std:: {
2
- fmt,
3
2
str:: { self , FromStr } ,
4
3
time:: { Duration , SystemTime , UNIX_EPOCH } ,
5
4
} ;
@@ -10,12 +9,24 @@ pub(crate) const RAW_LEN: usize = 12;
10
9
const ENCODED_LEN : usize = 20 ;
11
10
const ENC : & [ u8 ] = "0123456789abcdefghijklmnopqrstuv" . as_bytes ( ) ;
12
11
const DEC : [ u8 ; 256 ] = gen_dec ( ) ;
12
+ pub ( crate ) const ZERO : Id = Id ( [ 0u8 ; RAW_LEN ] ) ;
13
13
14
14
/// An ID.
15
- #[ derive( PartialEq , Eq , PartialOrd , Ord , Clone , Copy , Hash ) ]
15
+ #[ derive( PartialEq , Eq , PartialOrd , Ord , Clone , Copy , Hash , Default ) ]
16
16
pub struct Id ( pub [ u8 ; RAW_LEN ] ) ;
17
17
18
18
impl Id {
19
+ /// Create an Id from a bytes slice.
20
+ pub fn from_bytes ( bytes : & [ u8 ] ) -> Result < Self , ParseIdError > {
21
+ if bytes. len ( ) != RAW_LEN {
22
+ return Err ( ParseIdError :: InvalidLength ( bytes. len ( ) ) ) ;
23
+ }
24
+
25
+ let mut id = [ 0u8 ; RAW_LEN ] ;
26
+ id. copy_from_slice ( bytes) ;
27
+ Ok ( Id ( id) )
28
+ }
29
+
19
30
/// The binary representation of the id.
20
31
#[ must_use]
21
32
pub fn as_bytes ( & self ) -> & [ u8 ; RAW_LEN ] {
@@ -52,11 +63,16 @@ impl Id {
52
63
let raw = self . as_bytes ( ) ;
53
64
u32:: from_be_bytes ( [ 0 , raw[ 9 ] , raw[ 10 ] , raw[ 11 ] ] )
54
65
}
66
+
67
+ /// Returns true if this is a "zero" ID
68
+ pub fn is_zero ( & self ) -> bool {
69
+ self . 0 == ZERO . 0
70
+ }
55
71
}
56
72
57
- impl fmt:: Display for Id {
73
+ impl std :: fmt:: Display for Id {
58
74
// https://github.com/rs/xid/blob/efa678f304ab65d6d57eedcb086798381ae22206/id.go#L208
59
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
75
+ fn fmt ( & self , f : & mut std :: fmt:: Formatter < ' _ > ) -> std :: fmt:: Result {
60
76
let Self ( raw) = self ;
61
77
let mut bs = [ 0_u8 ; ENCODED_LEN ] ;
62
78
bs[ 19 ] = ENC [ ( ( raw[ 11 ] << 4 ) & 31 ) as usize ] ;
@@ -116,6 +132,11 @@ impl FromStr for Id {
116
132
let bs = s. as_bytes ( ) ;
117
133
let mut raw = [ 0_u8 ; RAW_LEN ] ;
118
134
raw[ 11 ] = DEC [ bs[ 17 ] as usize ] << 6 | DEC [ bs[ 18 ] as usize ] << 1 | DEC [ bs[ 19 ] as usize ] >> 4 ;
135
+ // check the last byte
136
+ if ENC [ ( ( raw[ 11 ] << 4 ) & 31 ) as usize ] != bs[ 19 ] {
137
+ return Err ( ParseIdError :: InvalidCharacter ( bs[ 19 ] as char ) ) ;
138
+ }
139
+
119
140
raw[ 10 ] = DEC [ bs[ 16 ] as usize ] << 3 | DEC [ bs[ 17 ] as usize ] >> 2 ;
120
141
raw[ 9 ] = DEC [ bs[ 14 ] as usize ] << 5 | DEC [ bs[ 15 ] as usize ] ;
121
142
raw[ 8 ] = DEC [ bs[ 12 ] as usize ] << 7 | DEC [ bs[ 13 ] as usize ] << 2 | DEC [ bs[ 14 ] as usize ] >> 3 ;
@@ -179,12 +200,28 @@ mod tests {
179
200
) ;
180
201
}
181
202
203
+ #[ test]
204
+ fn test_from_bytes_invalid_length ( ) {
205
+ assert_eq ! (
206
+ Id :: from_bytes( [ 1u8 ; 19 ] . as_slice( ) ) ,
207
+ Err ( ParseIdError :: InvalidLength ( 19 ) )
208
+ ) ;
209
+ }
210
+
182
211
#[ test]
183
212
fn test_from_str_invalid_char ( ) {
184
213
assert_eq ! (
185
214
Id :: from_str( "9z4e2mr0ui3e8a215n4g" ) ,
186
215
Err ( ParseIdError :: InvalidCharacter ( 'z' ) )
187
216
) ;
217
+
218
+ assert_eq ! (
219
+ Id :: from_str( "00000000000000jarvis" ) ,
220
+ Err ( ParseIdError :: InvalidCharacter ( 's' ) )
221
+ ) ;
222
+
223
+ assert ! ( Id :: from_str( "00000000000000jarvig" ) . is_ok( ) ) ;
224
+ assert ! ( !Id :: from_str( "00000000000000jarvig" ) . unwrap( ) . is_zero( ) ) ;
188
225
}
189
226
190
227
// https://github.com/rs/xid/blob/efa678f304ab65d6d57eedcb086798381ae22206/id_test.go#L45
@@ -237,6 +274,25 @@ mod tests {
237
274
assert_eq ! ( id. machine( ) , t. machine_id) ;
238
275
assert_eq ! ( id. pid( ) , t. pid) ;
239
276
assert_eq ! ( id. counter( ) , t. counter) ;
277
+
278
+ let rt = Id :: from_bytes ( id. as_bytes ( ) ) ;
279
+ assert ! ( rt. is_ok( ) ) ;
280
+ assert_eq ! ( rt. unwrap( ) , id) ;
240
281
}
241
282
}
283
+
284
+ #[ test]
285
+ fn test_default ( ) {
286
+ let id: Id = Default :: default ( ) ;
287
+ assert ! ( id. is_zero( ) ) ;
288
+ assert_eq ! ( "00000000000000000000" , id. to_string( ) . as_str( ) ) ;
289
+ assert_eq ! ( Id :: from_str( "00000000000000000000" ) . unwrap( ) , id) ;
290
+ assert_eq ! (
291
+ id. time( ) . duration_since( UNIX_EPOCH ) . unwrap( ) . as_secs( ) ,
292
+ 0u64 ,
293
+ ) ;
294
+ assert_eq ! ( id. machine( ) , [ 0u8 ; 3 ] ) ;
295
+ assert_eq ! ( id. pid( ) , 0u16 ) ;
296
+ assert_eq ! ( id. counter( ) , 0u32 ) ;
297
+ }
242
298
}
0 commit comments