Skip to main content

ps_uuid/methods/
fmt_urn.rs

1//! URN formatting for UUID.
2
3use core::fmt;
4
5use crate::UUID;
6
7/// A UUID formatted as a Uniform Resource Name.
8///
9/// Created by calling [`UUID::urn()`], this wrapper implements [`Display`]
10/// to render the UUID with the `urn:uuid:` prefix as specified in RFC 4122.
11///
12/// ```text
13/// urn:uuid:550e8400-e29b-41d4-a716-446655440000
14/// ```
15///
16/// [`Display`]: core::fmt::Display
17#[derive(Clone, Copy, Debug)]
18pub struct Urn(UUID);
19
20impl fmt::Display for Urn {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        let b = &self.0.bytes;
23        write!(
24            f,
25            "urn:uuid:{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
26            b[0], b[1], b[2], b[3],
27            b[4], b[5],
28            b[6], b[7],
29            b[8], b[9],
30            b[10], b[11], b[12], b[13], b[14], b[15]
31        )
32    }
33}
34
35impl From<Urn> for UUID {
36    #[inline]
37    fn from(urn: Urn) -> Self {
38        urn.0
39    }
40}
41
42impl UUID {
43    /// Returns a formatter for the URN format.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// use ps_uuid::UUID;
49    ///
50    /// let uuid = UUID::nil();
51    /// assert_eq!(uuid.urn().to_string(), "urn:uuid:00000000-0000-0000-0000-000000000000");
52    /// ```
53    #[inline]
54    #[must_use]
55    pub const fn urn(self) -> Urn {
56        Urn(self)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    #![allow(clippy::expect_used)]
63    use crate::UUID;
64
65    #[test]
66    fn nil_formats_correctly() {
67        assert_eq!(
68            UUID::nil().urn().to_string(),
69            "urn:uuid:00000000-0000-0000-0000-000000000000"
70        );
71    }
72
73    #[test]
74    fn max_formats_correctly() {
75        assert_eq!(
76            UUID::max().urn().to_string(),
77            "urn:uuid:ffffffff-ffff-ffff-ffff-ffffffffffff"
78        );
79    }
80
81    #[test]
82    fn urn_has_correct_length() {
83        let uuid = UUID::gen_v4();
84        let urn = uuid.urn().to_string();
85        assert_eq!(urn.len(), 45); // "urn:uuid:" (9) + hyphenated (36) = 45
86    }
87
88    #[test]
89    fn urn_starts_with_prefix() {
90        let uuid = UUID::gen_v4();
91        let urn = uuid.urn().to_string();
92        assert!(urn.starts_with("urn:uuid:"));
93    }
94
95    #[test]
96    fn urn_contains_hyphenated_uuid() {
97        let uuid = UUID::gen_v4();
98        let urn = uuid.urn().to_string();
99        let hyphenated = uuid.hyphenated().to_string();
100        assert!(urn.ends_with(&hyphenated));
101    }
102
103    #[test]
104    fn urn_is_lowercase() {
105        let uuid = UUID::from_bytes([
106            0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56,
107            0x78, 0x9A,
108        ]);
109        let urn = uuid.urn().to_string();
110        assert_eq!(urn, "urn:uuid:abcdef12-3456-789a-bcde-f0123456789a");
111    }
112
113    #[test]
114    fn round_trip_parse() {
115        let uuid = UUID::gen_v4();
116        let urn = uuid.urn().to_string();
117        let parsed: UUID = urn.parse().expect("URN format should parse");
118        assert_eq!(parsed, uuid);
119    }
120}