Skip to content

Commit 875aa31

Browse files
committed
feat(rust/signed-doc): add types for UUIDv4 and UUIDv7
1 parent d942e1e commit 875aa31

File tree

4 files changed

+156
-0
lines changed

4 files changed

+156
-0
lines changed

rust/signed_doc/src/metadata/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
use std::fmt::{Display, Formatter};
33

44
mod document_ref;
5+
mod uuid_type;
56

67
pub use document_ref::DocumentRef;
8+
pub use uuid_type::{UuidV4, UuidV7};
79

810
/// Document Metadata.
911
#[derive(Debug, serde::Deserialize)]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//! `UUID` types.
2+
3+
mod uuid_v4;
4+
mod uuid_v7;
5+
6+
pub use uuid_v4::UuidV4;
7+
pub use uuid_v7::UuidV7;
8+
9+
/// Invalid Doc Type UUID
10+
pub(crate) const INVALID_UUID: uuid::Uuid = uuid::Uuid::from_bytes([0x00; 16]);
11+
12+
/// CBOR tag for UUID content.
13+
const UUID_CBOR_TAG: u64 = 37;
14+
15+
/// Decode `CBOR` encoded `UUID`.
16+
pub(crate) fn decode_cbor_uuid(val: &coset::cbor::Value) -> anyhow::Result<uuid::Uuid> {
17+
let Some((UUID_CBOR_TAG, coset::cbor::Value::Bytes(bytes))) = val.as_tag() else {
18+
anyhow::bail!("Invalid CBOR encoded UUID type");
19+
};
20+
let uuid = uuid::Uuid::from_bytes(
21+
bytes
22+
.clone()
23+
.try_into()
24+
.map_err(|_| anyhow::anyhow!("Invalid CBOR encoded UUID type, invalid bytes size"))?,
25+
);
26+
Ok(uuid)
27+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//! `UUIDv4` Type.
2+
use std::fmt::{Display, Formatter};
3+
4+
use super::{decode_cbor_uuid, INVALID_UUID};
5+
6+
/// Type representing a `UUIDv4`.
7+
#[derive(Copy, Clone, Debug, serde::Deserialize)]
8+
#[serde(transparent)]
9+
pub struct UuidV4 {
10+
/// UUID
11+
uuid: uuid::Uuid,
12+
}
13+
14+
impl UuidV4 {
15+
/// Version for `UUIDv4`.
16+
const UUID_VERSION_NUMBER: usize = 4;
17+
18+
/// Generates a zeroed out `UUIDv4` that can never be valid.
19+
pub fn invalid() -> Self {
20+
Self { uuid: INVALID_UUID }
21+
}
22+
23+
/// Check if this is a valid `UUIDv4`.
24+
pub fn is_valid(&self) -> bool {
25+
self.uuid != INVALID_UUID || self.uuid.get_version_num() == Self::UUID_VERSION_NUMBER
26+
}
27+
28+
/// Returns the `uuid::Uuid` type.
29+
#[must_use]
30+
pub fn uuid(&self) -> uuid::Uuid {
31+
self.uuid
32+
}
33+
}
34+
35+
impl Display for UuidV4 {
36+
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
37+
write!(f, "{}", self.uuid)
38+
}
39+
}
40+
41+
impl TryFrom<&coset::cbor::Value> for UuidV4 {
42+
type Error = anyhow::Error;
43+
44+
fn try_from(cbor_value: &coset::cbor::Value) -> Result<Self, Self::Error> {
45+
match decode_cbor_uuid(cbor_value) {
46+
Ok(uuid) => {
47+
if uuid.get_version_num() == Self::UUID_VERSION_NUMBER {
48+
Ok(Self { uuid })
49+
} else {
50+
anyhow::bail!("UUID {uuid} is not `v{}`", Self::UUID_VERSION_NUMBER);
51+
}
52+
},
53+
Err(e) => {
54+
anyhow::bail!("Invalid UUID. Error: {e}");
55+
},
56+
}
57+
}
58+
}
59+
60+
/// Returns a `UUIDv4` from `uuid::Uuid`.
61+
///
62+
/// NOTE: This does not guarantee that the `UUID` is valid.
63+
impl From<uuid::Uuid> for UuidV4 {
64+
fn from(uuid: uuid::Uuid) -> Self {
65+
Self { uuid }
66+
}
67+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! `UUIDv7` Type.
2+
use std::fmt::{Display, Formatter};
3+
4+
use super::{decode_cbor_uuid, INVALID_UUID};
5+
6+
/// Type representing a `UUIDv7`.
7+
#[derive(Copy, Clone, Debug, serde::Deserialize, PartialEq, PartialOrd)]
8+
#[serde(transparent)]
9+
pub struct UuidV7 {
10+
/// UUID
11+
uuid: uuid::Uuid,
12+
}
13+
14+
impl UuidV7 {
15+
/// Version for `UUIDv7`.
16+
const UUID_VERSION_NUMBER: usize = 7;
17+
18+
/// Generates a zeroed out `UUIDv7` that can never be valid.
19+
#[must_use]
20+
pub fn invalid() -> Self {
21+
Self { uuid: INVALID_UUID }
22+
}
23+
24+
/// Check if this is a valid `UUIDv7`.
25+
#[must_use]
26+
pub fn is_valid(&self) -> bool {
27+
self.uuid != INVALID_UUID || self.uuid.get_version_num() == Self::UUID_VERSION_NUMBER
28+
}
29+
30+
/// Returns the `uuid::Uuid` type.
31+
#[must_use]
32+
pub fn uuid(&self) -> uuid::Uuid {
33+
self.uuid
34+
}
35+
}
36+
37+
impl Display for UuidV7 {
38+
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
39+
write!(f, "{}", self.uuid)
40+
}
41+
}
42+
43+
impl TryFrom<&coset::cbor::Value> for UuidV7 {
44+
type Error = anyhow::Error;
45+
46+
fn try_from(cbor_value: &coset::cbor::Value) -> Result<Self, Self::Error> {
47+
match decode_cbor_uuid(cbor_value) {
48+
Ok(uuid) => {
49+
if uuid.get_version_num() == Self::UUID_VERSION_NUMBER {
50+
Ok(Self { uuid })
51+
} else {
52+
anyhow::bail!("UUID {uuid} is not `v{}`", Self::UUID_VERSION_NUMBER);
53+
}
54+
},
55+
Err(e) => {
56+
anyhow::bail!("Invalid UUID. Error: {e}");
57+
},
58+
}
59+
}
60+
}

0 commit comments

Comments
 (0)