Skip to content

Commit 8f27dc2

Browse files
authored
Merge pull request #10 from yiwen-ai/main
Add Default/`from_bytes`/`is_zero` and check the last byte in `Id::from_str`
2 parents 76b8ca8 + 8e96fc3 commit 8f27dc2

File tree

1 file changed

+60
-4
lines changed

1 file changed

+60
-4
lines changed

src/id.rs

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::{
2-
fmt,
32
str::{self, FromStr},
43
time::{Duration, SystemTime, UNIX_EPOCH},
54
};
@@ -10,12 +9,24 @@ pub(crate) const RAW_LEN: usize = 12;
109
const ENCODED_LEN: usize = 20;
1110
const ENC: &[u8] = "0123456789abcdefghijklmnopqrstuv".as_bytes();
1211
const DEC: [u8; 256] = gen_dec();
12+
pub(crate) const ZERO: Id = Id([0u8; RAW_LEN]);
1313

1414
/// An ID.
15-
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
15+
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash, Default)]
1616
pub struct Id(pub [u8; RAW_LEN]);
1717

1818
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+
1930
/// The binary representation of the id.
2031
#[must_use]
2132
pub fn as_bytes(&self) -> &[u8; RAW_LEN] {
@@ -52,11 +63,16 @@ impl Id {
5263
let raw = self.as_bytes();
5364
u32::from_be_bytes([0, raw[9], raw[10], raw[11]])
5465
}
66+
67+
/// Returns true if this is a "zero" ID
68+
pub fn is_zero(&self) -> bool {
69+
self.0 == ZERO.0
70+
}
5571
}
5672

57-
impl fmt::Display for Id {
73+
impl std::fmt::Display for Id {
5874
// 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 {
6076
let Self(raw) = self;
6177
let mut bs = [0_u8; ENCODED_LEN];
6278
bs[19] = ENC[((raw[11] << 4) & 31) as usize];
@@ -116,6 +132,11 @@ impl FromStr for Id {
116132
let bs = s.as_bytes();
117133
let mut raw = [0_u8; RAW_LEN];
118134
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+
119140
raw[10] = DEC[bs[16] as usize] << 3 | DEC[bs[17] as usize] >> 2;
120141
raw[9] = DEC[bs[14] as usize] << 5 | DEC[bs[15] as usize];
121142
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 {
179200
);
180201
}
181202

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+
182211
#[test]
183212
fn test_from_str_invalid_char() {
184213
assert_eq!(
185214
Id::from_str("9z4e2mr0ui3e8a215n4g"),
186215
Err(ParseIdError::InvalidCharacter('z'))
187216
);
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());
188225
}
189226

190227
// https://github.com/rs/xid/blob/efa678f304ab65d6d57eedcb086798381ae22206/id_test.go#L45
@@ -237,6 +274,25 @@ mod tests {
237274
assert_eq!(id.machine(), t.machine_id);
238275
assert_eq!(id.pid(), t.pid);
239276
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);
240281
}
241282
}
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+
}
242298
}

0 commit comments

Comments
 (0)