make ctors that accept slices infallible using arrays

This commit is contained in:
KodrAus 2021-10-29 12:08:17 +10:00
parent 07f920f24b
commit 20e253a124
7 changed files with 225 additions and 170 deletions

View File

@ -51,34 +51,56 @@ status = "actively-developed"
[badges.travis-ci]
repository = "uuid-rs/uuid"
[features]
default = ["std"]
guid = ["winapi"]
std = []
stdweb = ["getrandom", "getrandom/js"]
v1 = ["atomic"]
v3 = ["md-5"]
v4 = ["getrandom"]
v5 = ["sha-1"]
wasm-bindgen = ["getrandom", "getrandom/js"]
# Private
[dependencies.getrandom]
optional = true
version = "0.2"
# Private
[dependencies.atomic]
default-features = false
optional = true
version = "0.5"
# Private
[dependencies.md-5]
default-features = false
optional = true
version = "0.9"
[dependencies.serde]
default-features = false
optional = true
version = "1.0.56"
# Private
[dependencies.sha-1]
default-features = false
optional = true
version = "0.9"
# Public: Used in trait impls on `Uuid`
[dependencies.serde]
default-features = false
optional = true
version = "1.0.56"
# Public: Used in trait impls on `Uuid`
[dependencies.slog]
optional = true
version = "2"
# Public: Used in `From` impls on `Uuid`
[target.'cfg(windows)'.dependencies.winapi]
optional = true
version = "0.3"
[dev-dependencies.bincode]
version = "1.0"
@ -97,18 +119,3 @@ version = "0.2"
[dev-dependencies.wasm-bindgen-test]
version = "0.3"
[features]
default = ["std"]
guid = ["winapi"]
std = []
stdweb = ["getrandom", "getrandom/js"]
v1 = ["atomic"]
v3 = ["md-5"]
v4 = ["getrandom"]
v5 = ["sha-1"]
wasm-bindgen = ["getrandom", "getrandom/js"]
[target.'cfg(windows)'.dependencies.winapi]
optional = true
version = "0.3"

View File

@ -46,10 +46,6 @@ impl Uuid {
/// Creates a UUID from four field values.
///
/// # Errors
///
/// This function will return an error if `d4`'s length is not 8 bytes.
///
/// # Examples
///
/// Basic usage:
@ -57,31 +53,25 @@ impl Uuid {
/// ```
/// use uuid::Uuid;
///
/// let d1 = 0xAB3F1097u32;
/// let d2 = 0x501Eu16;
/// let d3 = 0xB736u16;
/// let d4 = [12, 3, 9, 56, 54, 43, 8, 9];
///
/// let uuid = Uuid::from_fields(42, 12, 5, &d4);
/// let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
/// let uuid = Uuid::from_fields(d1, d2, d3, &d4);
///
/// let expected_uuid =
/// Ok(String::from("0000002a-000c-0005-0c03-0938362b0809"));
///
/// assert_eq!(expected_uuid, uuid);
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "ab3f1097-501e-b736-0c03-0938362b0809"
/// );
/// ```
pub fn from_fields(
pub const fn from_fields(
d1: u32,
d2: u16,
d3: u16,
d4: &[u8],
) -> Result<Uuid, crate::Error> {
const D4_LEN: usize = 8;
let len = d4.len();
if len != D4_LEN {
return crate::err(Error::new(D4_LEN, len));
}
Ok(Uuid::from_bytes([
d4: &[u8; 8],
) -> Uuid {
Uuid::from_bytes([
(d1 >> 24) as u8,
(d1 >> 16) as u8,
(d1 >> 8) as u8,
@ -98,7 +88,7 @@ impl Uuid {
d4[5],
d4[6],
d4[7],
]))
])
}
/// Creates a UUID from four field values in little-endian order.
@ -119,28 +109,19 @@ impl Uuid {
/// let d4 = [12, 3, 9, 56, 54, 43, 8, 9];
///
/// let uuid = Uuid::from_fields_le(d1, d2, d3, &d4);
/// let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
///
/// let expected_uuid =
/// Ok(String::from("97103fab-1e50-36b7-0c03-0938362b0809"));
///
/// assert_eq!(expected_uuid, uuid);
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "97103fab-1e50-36b7-0c03-0938362b0809"
/// );
/// ```
pub fn from_fields_le(
pub const fn from_fields_le(
d1: u32,
d2: u16,
d3: u16,
d4: &[u8],
) -> Result<Uuid, crate::Error> {
const D4_LEN: usize = 8;
let len = d4.len();
if len != D4_LEN {
return crate::err(Error::new(D4_LEN, len));
}
Ok(Uuid::from_bytes([
) -> Uuid {
Uuid::from_bytes([
d1 as u8,
(d1 >> 8) as u8,
(d1 >> 16) as u8,
@ -157,10 +138,27 @@ impl Uuid {
d4[5],
d4[6],
d4[7],
]))
])
}
/// Creates a UUID from a 128bit value.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use uuid::Uuid;
///
/// let v = 0xa1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8u128;
///
/// let uuid = Uuid::from_u128(v);
///
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"
/// );
/// ```
pub const fn from_u128(v: u128) -> Self {
Uuid::from_bytes([
(v >> 120) as u8,
@ -188,6 +186,23 @@ impl Uuid {
/// This is based on the endianness of the UUID, rather than the target
/// environment so bytes will be flipped on both big and little endian
/// machines.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use uuid::Uuid;
///
/// let v = 0xd8d7d6d5d4d3d2d1c2c1b2b1a4a3a2a1u128;
///
/// let uuid = Uuid::from_u128_le(v);
///
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"
/// );
/// ```
pub const fn from_u128_le(v: u128) -> Self {
Uuid::from_bytes([
v as u8,
@ -210,6 +225,24 @@ impl Uuid {
}
/// Creates a UUID from two 64bit values.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use uuid::Uuid;
///
/// let hi = 0xa1a2a3a4b1b2c1c2u64;
/// let lo = 0xd1d2d3d4d5d6d7d8u64;
///
/// let uuid = Uuid::from_u64_pair(hi, lo);
///
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"
/// );
/// ```
pub const fn from_u64_pair(high_bits: u64, low_bits: u64) -> Self {
Uuid::from_bytes([
(high_bits >> 56) as u8,
@ -242,22 +275,25 @@ impl Uuid {
/// Basic usage:
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use uuid::Uuid;
///
/// let bytes = [4, 54, 67, 12, 43, 2, 98, 76, 32, 50, 87, 5, 1, 33, 43, 87];
///
/// let uuid = Uuid::from_slice(&bytes);
/// let uuid = uuid.map(|uuid| uuid.to_hyphenated().to_string());
/// let uuid = Uuid::from_slice(&bytes)?;
///
/// let expected_uuid =
/// Ok(String::from("0436430c-2b02-624c-2032-570501212b57"));
///
/// assert_eq!(expected_uuid, uuid);
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "0436430c-2b02-624c-2032-570501212b57"
/// );
/// # Ok(())
/// # }
/// ```
///
/// An incorrect number of bytes:
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use uuid::Uuid;
///
/// let bytes = [4, 54, 67, 12, 43, 2, 98, 76];
@ -265,6 +301,8 @@ impl Uuid {
/// let uuid = Uuid::from_slice(&bytes);
///
/// assert!(uuid.is_err());
/// # Ok(())
/// # }
/// ```
pub fn from_slice(b: &[u8]) -> Result<Uuid, crate::Error> {
const BYTES_LEN: usize = 16;
@ -281,32 +319,32 @@ impl Uuid {
}
/// Creates a UUID using the supplied bytes.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use uuid::Uuid;
///
/// let bytes = [4, 54, 67, 12, 43, 2, 98, 76, 32, 50, 87, 5, 1, 33, 43, 87];
///
/// let uuid = Uuid::from_bytes(bytes);
///
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "0436430c-2b02-624c-2032-570501212b57"
/// );
/// # Ok(())
/// # }
/// ```
pub const fn from_bytes(bytes: Bytes) -> Uuid {
Uuid(bytes)
}
}
/// A builder struct for creating a UUID.
///
/// # Examples
///
/// Creating a v4 UUID from externally generated bytes:
///
/// ```
/// use uuid::{Builder, Variant, Version};
///
/// # let rng = || [
/// # 70, 235, 208, 238, 14, 109, 67, 201, 185, 13, 204, 195, 90,
/// # 145, 63, 62,
/// # ];
/// let random_bytes = rng();
/// let uuid = Builder::from_bytes(random_bytes)
/// .set_variant(Variant::RFC4122)
/// .set_version(Version::Random)
/// .build();
/// ```
impl crate::Builder {
impl Builder {
/// Creates a `Builder` using the supplied bytes.
///
/// # Examples
@ -385,52 +423,63 @@ impl crate::Builder {
/// Creates a `Builder` from four field values.
///
/// # Errors
/// # Examples
///
/// This function will return an error if `d4`'s length is not 8 bytes.
/// Basic usage:
///
/// ```
/// let d1 = 0xAB3F1097u32;
/// let d2 = 0x501Eu16;
/// let d3 = 0xB736u16;
/// let d4 = [12, 3, 9, 56, 54, 43, 8, 9];
///
/// let uuid = uuid::Builder::from_fields(d1, d2, d3, &d4).into_uuid();
///
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "ab3f1097-501e-b736-0c03-0938362b0809"
/// );
/// ```
pub const fn from_fields(
d1: u32,
d2: u16,
d3: u16,
d4: &[u8; 8],
) -> Self {
Builder::from_bytes(*Uuid::from_fields(d1, d2, d3, d4).as_bytes())
}
/// Creates a `Builder` from four field values.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let d1 = 0xAB3F1097u32;
/// let d2 = 0x501Eu16;
/// let d3 = 0xB736u16;
/// let d4 = [12, 3, 9, 56, 54, 43, 8, 9];
///
/// let builder = uuid::Builder::from_fields(42, 12, 5, &d4);
/// let uuid =
/// builder.map(|mut builder| builder.build().to_hyphenated().to_string());
/// let uuid = uuid::Builder::from_fields_le(d1, d2, d3, &d4).into_uuid();
///
/// let expected_uuid =
/// Ok(String::from("0000002a-000c-0005-0c03-0938362b0809"));
///
/// assert_eq!(expected_uuid, uuid);
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
/// "97103fab-1e50-36b7-0c03-0938362b0809"
/// );
/// ```
///
/// An invalid length:
///
/// ```
/// let d4 = [12];
///
/// let builder = uuid::Builder::from_fields(42, 12, 5, &d4);
///
/// assert!(builder.is_err());
/// ```
pub fn from_fields(
pub const fn from_fields_le(
d1: u32,
d2: u16,
d3: u16,
d4: &[u8],
) -> Result<Self, crate::Error> {
Uuid::from_fields(d1, d2, d3, d4).map(|uuid| {
let bytes = *uuid.as_bytes();
crate::Builder::from_bytes(bytes)
})
d4: &[u8; 8],
) -> Self {
Builder::from_bytes(*Uuid::from_fields_le(d1, d2, d3, d4).as_bytes())
}
/// Creates a `Builder` from a 128bit value.
pub fn from_u128(v: u128) -> Self {
crate::Builder::from_bytes(*Uuid::from_u128(v).as_bytes())
pub const fn from_u128(v: u128) -> Self {
Builder::from_bytes(*Uuid::from_u128(v).as_bytes())
}
/// Creates a `Builder` with an initial [`Uuid::nil`].
@ -467,6 +516,20 @@ impl crate::Builder {
self
}
/// Specifies the variant of the UUID.
pub const fn with_variant(mut self, v: crate::Variant) -> Self {
let byte = self.0[8];
self.0[8] = match v {
crate::Variant::NCS => byte & 0x7f,
crate::Variant::RFC4122 => (byte & 0x3f) | 0x80,
crate::Variant::Microsoft => (byte & 0x1f) | 0xc0,
crate::Variant::Future => (byte & 0x1f) | 0xe0,
};
self
}
/// Specifies the version number of the UUID.
pub fn set_version(&mut self, v: crate::Version) -> &mut Self {
self.0[6] = (self.0[6] & 0x0f) | ((v as u8) << 4);
@ -474,6 +537,13 @@ impl crate::Builder {
self
}
/// Specifies the version number of the UUID.
pub const fn with_version(mut self, v: crate::Version) -> Self {
self.0[6] = (self.0[6] & 0x0f) | ((v as u8) << 4);
self
}
/// Hands over the internal constructed [`Uuid`].
///
/// # Examples
@ -495,4 +565,9 @@ impl crate::Builder {
pub fn build(&mut self) -> Uuid {
Uuid::from_bytes(self.0)
}
/// Convert the builder into a [`Uuid`].
pub const fn into_uuid(self) -> Uuid {
Uuid::from_bytes(self.0)
}
}

View File

@ -11,8 +11,6 @@ pub(crate) fn err<T>(err: impl Into<Error>) -> Result<T, Error> {
Err(err.into())
}
// TODO: write tests for Error
// BODY: not immediately blocking, but should be covered for 1.0
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
enum Inner {
/// An error occurred while handling [`Uuid`] bytes.

View File

@ -209,7 +209,7 @@ mod v5;
mod winapi_support;
use crate::{
error::err,
error::*,
std::{convert, fmt, str},
};
@ -953,7 +953,7 @@ mod tests {
let d3: u16 = 0xc1c2;
let d4 = [0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8];
let u = Uuid::from_fields(d1, d2, d3, &d4).unwrap();
let u = Uuid::from_fields(d1, d2, d3, &d4);
let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8";
let result = u.to_simple().to_string();
@ -968,7 +968,7 @@ mod tests {
let d3: u16 = 0xc2c1;
let d4 = [0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8];
let u = Uuid::from_fields_le(d1, d2, d3, &d4).unwrap();
let u = Uuid::from_fields_le(d1, d2, d3, &d4);
let expected = "a1a2a3a4b1b2c1c2d1d2d3d4d5d6d7d8";
let result = u.to_simple().to_string();
@ -996,7 +996,7 @@ mod tests {
let d3_in: u16 = 0xc1c2;
let d4_in = &[0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8];
let u = Uuid::from_fields(d1_in, d2_in, d3_in, d4_in).unwrap();
let u = Uuid::from_fields(d1_in, d2_in, d3_in, d4_in);
let (d1_out, d2_out, d3_out, d4_out) = u.as_fields();
assert_eq!(d1_in, d1_out);
@ -1013,7 +1013,7 @@ mod tests {
let d3_in: u16 = 0xc2c1;
let d4_in = &[0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8];
let u = Uuid::from_fields_le(d1_in, d2_in, d3_in, d4_in).unwrap();
let u = Uuid::from_fields_le(d1_in, d2_in, d3_in, d4_in);
let (d1_out, d2_out, d3_out, d4_out) = u.to_fields_le();
assert_eq!(d1_in, d1_out);
@ -1030,7 +1030,7 @@ mod tests {
let d3_in: u16 = 0xc1c2;
let d4_in = &[0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8];
let u = Uuid::from_fields(d1_in, d2_in, d3_in, d4_in).unwrap();
let u = Uuid::from_fields(d1_in, d2_in, d3_in, d4_in);
let (d1_out, d2_out, d3_out, d4_out) = u.to_fields_le();
assert_eq!(d1_in, d1_out.swap_bytes());

View File

@ -29,19 +29,6 @@ fn len_matches_any(len: usize, crits: &[usize]) -> bool {
false
}
/// Check if the length matches any criteria lengths in the given range
/// (inclusive).
#[allow(dead_code)]
fn len_matches_range(len: usize, min: usize, max: usize) -> bool {
for crit in min..=max {
if len == crit {
return true;
}
}
false
}
// Accumulated length of each hyphenated group in hex digits.
const ACC_GROUP_LENS: [usize; 5] = [8, 12, 16, 20, 32];

View File

@ -164,7 +164,7 @@ impl Uuid {
///
/// let context = Context::new(42);
/// let ts = Timestamp::from_unix(&context, 1497624119, 1234);
/// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]).expect("failed to generate UUID");
/// let uuid = Uuid::new_v1(ts, &[1, 2, 3, 4, 5, 6]);
///
/// assert_eq!(
/// uuid.to_hyphenated().to_string(),
@ -191,14 +191,7 @@ impl Uuid {
/// [`Timestamp`]: v1/struct.Timestamp.html
/// [`ClockSequence`]: v1/struct.ClockSequence.html
/// [`Context`]: v1/struct.Context.html
pub fn new_v1(ts: Timestamp, node_id: &[u8]) -> Result<Self, crate::Error> {
const NODE_ID_LEN: usize = 6;
let len = node_id.len();
if len != NODE_ID_LEN {
return crate::err(crate::builder::Error::new(NODE_ID_LEN, len));
}
pub fn new_v1(ts: Timestamp, node_id: &[u8; 6]) -> Self {
let time_low = (ts.ticks & 0xFFFF_FFFF) as u32;
let time_mid = ((ts.ticks >> 32) & 0xFFFF) as u16;
let time_high_and_version =
@ -229,28 +222,25 @@ impl Uuid {
/// value into more commonly-used formats, such as a unix timestamp.
///
/// [`Timestamp`]: v1/struct.Timestamp.html
pub fn to_timestamp(&self) -> Option<Timestamp> {
if self
.get_version()
.map(|v| v != Version::Mac)
.unwrap_or(true)
{
return None;
pub const fn to_timestamp(&self) -> Option<Timestamp> {
match self.get_version() {
Some(Version::Mac) => {
let ticks: u64 = ((self.as_bytes()[6] & 0x0F) as u64) << 56
| ((self.as_bytes()[7]) as u64) << 48
| ((self.as_bytes()[4]) as u64) << 40
| ((self.as_bytes()[5]) as u64) << 32
| ((self.as_bytes()[0]) as u64) << 24
| ((self.as_bytes()[1]) as u64) << 16
| ((self.as_bytes()[2]) as u64) << 8
| (self.as_bytes()[3] as u64);
let counter: u16 = ((self.as_bytes()[8] & 0x3F) as u16) << 8
| (self.as_bytes()[9] as u16);
Some(Timestamp::from_rfc4122(ticks, counter))
},
_ => None
}
let ticks: u64 = u64::from(self.as_bytes()[6] & 0x0F) << 56
| u64::from(self.as_bytes()[7]) << 48
| u64::from(self.as_bytes()[4]) << 40
| u64::from(self.as_bytes()[5]) << 32
| u64::from(self.as_bytes()[0]) << 24
| u64::from(self.as_bytes()[1]) << 16
| u64::from(self.as_bytes()[2]) << 8
| u64::from(self.as_bytes()[3]);
let counter: u16 = u16::from(self.as_bytes()[8] & 0x3F) << 8
| u16::from(self.as_bytes()[9]);
Some(Timestamp::from_rfc4122(ticks, counter))
}
}

View File

@ -6,15 +6,13 @@ impl Uuid {
/// Converts a little endian winapi `GUID` into a [`Uuid`]
///
/// [`Uuid`]: ../struct.Uuid.html
pub fn from_guid(guid: guiddef::GUID) -> Self {
pub const fn from_guid(guid: guiddef::GUID) -> Self {
Uuid::from_fields_le(
guid.Data1 as u32,
guid.Data2 as u16,
guid.Data3 as u16,
&(guid.Data4 as [u8; 8]),
)
.unwrap() // Note: The result in this particular instance is always Ok,
// so we can safely unwrap.
}
/// Converts a [`Uuid`] into a little endian winapi `GUID`