Skip to main content

ps_uuid/methods/
with_version.rs

1use crate::UUID;
2
3impl UUID {
4    #[must_use]
5    pub const fn with_version(self, version: u8) -> Self {
6        let mut uuid = self;
7
8        uuid.bytes[6] &= 0x0F;
9        uuid.bytes[6] |= version << 4;
10
11        uuid.with_variant(crate::Variant::OSF)
12    }
13}
14
15#[cfg(test)]
16mod tests {
17    use super::UUID;
18
19    // Helper function to create a UUID with known bytes for testing
20    const fn create_test_uuid(bytes: [u8; 16]) -> UUID {
21        UUID { bytes }
22    }
23
24    // Helper function to check if a UUID has the OSF variant (10xx_xxxx in byte 8)
25    const fn has_osf_variant(uuid: &UUID) -> bool {
26        let byte8 = uuid.bytes[8];
27        (byte8 & 0b1100_0000) == 0b1000_0000
28    }
29
30    #[test]
31    fn test_set_valid_version() {
32        // Test setting versions 1–5 (common UUID versions)
33        let original_bytes = [
34            0x12, 0x34, 0x56, 0x78, // time_low
35            0x9A, 0xBC, // time_mid
36            0xDE, 0xF0, // time_hi_and_version, clock_seq_hi_and_reserved
37            0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // node
38        ];
39        let uuid = create_test_uuid(original_bytes);
40
41        for version in 1..=5 {
42            let modified_uuid = uuid.with_version(version);
43            // Check version is set correctly using version()
44            assert_eq!(
45                modified_uuid.get_version(),
46                Some(version),
47                "Version should be set to {version}"
48            );
49            // Check byte 6: upper 4 bits = version, lower 4 bits preserved (0xE from 0xDE)
50            assert_eq!(
51                modified_uuid.bytes[6],
52                (version << 4) | (original_bytes[6] & 0x0F),
53                "Byte 6 incorrect for version {version}"
54            );
55            // Verify OSF variant
56            assert!(
57                has_osf_variant(&modified_uuid),
58                "OSF variant not set for version {version}"
59            );
60            // Check other bytes are unchanged
61            for (i, _) in original_bytes.iter().enumerate() {
62                if i != 6 && i != 8 {
63                    assert_eq!(
64                        modified_uuid.bytes[i], original_bytes[i],
65                        "Byte {i} changed unexpectedly for version {version}"
66                    );
67                }
68            }
69        }
70    }
71
72    #[test]
73    fn test_set_version_zero() {
74        // Test setting version 0 (valid but uncommon)
75        let original_bytes = [0xFF; 16];
76        let uuid = create_test_uuid(original_bytes);
77
78        let modified_uuid = uuid.with_version(0);
79        assert_eq!(
80            modified_uuid.get_version(),
81            Some(0),
82            "Version should be set to 0"
83        );
84        assert_eq!(
85            modified_uuid.bytes[6],
86            original_bytes[6] & 0x0F,
87            "Byte 6 should preserve lower bits with version 0"
88        );
89        assert!(
90            has_osf_variant(&modified_uuid),
91            "OSF variant not set for version 0"
92        );
93        // Check other bytes unchanged except bytes 6 and 8
94        for (i, _) in original_bytes.iter().enumerate() {
95            if i != 6 && i != 8 {
96                assert_eq!(
97                    modified_uuid.bytes[i], original_bytes[i],
98                    "Byte {i} changed unexpectedly"
99                );
100            }
101        }
102    }
103
104    #[test]
105    fn test_set_max_version() {
106        // Test setting version 15 (maximum possible value)
107        let original_bytes = [0x00; 16];
108        let uuid = create_test_uuid(original_bytes);
109
110        let modified_uuid = uuid.with_version(15);
111        assert_eq!(
112            modified_uuid.get_version(),
113            Some(15),
114            "Version should be set to 15"
115        );
116        assert_eq!(
117            modified_uuid.bytes[6], 0xF0,
118            "Byte 6 should be 0xF0 for version 15"
119        );
120        assert!(
121            has_osf_variant(&modified_uuid),
122            "OSF variant not set for version 15"
123        );
124        // Check other bytes unchanged except bytes 6 and 8
125        for (i, _) in original_bytes.iter().enumerate() {
126            if i != 6 && i != 8 {
127                assert_eq!(
128                    modified_uuid.bytes[i], original_bytes[i],
129                    "Byte {i} changed unexpectedly"
130                );
131            }
132        }
133    }
134
135    #[test]
136    fn test_preserve_lower_nibble_byte6() {
137        // Test that lower 4 bits of byte 6 are preserved
138        let original_bytes = [
139            0x12, 0x34, 0x56, 0x78, // time_low
140            0x9A, 0xBC, // time_mid
141            0x5F, 0xFF, // time_hi_and_version (0x5F), clock_seq
142            0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, // node
143        ];
144        let uuid = create_test_uuid(original_bytes);
145
146        let modified_uuid = uuid.with_version(3);
147        assert_eq!(
148            modified_uuid.get_version(),
149            Some(3),
150            "Version should be set to 3"
151        );
152        assert_eq!(
153            modified_uuid.bytes[6], 0x3F,
154            "Byte 6 should have version 3 (0x3) in upper nibble and preserve 0xF in lower nibble"
155        );
156        assert!(has_osf_variant(&modified_uuid), "OSF variant not set");
157    }
158
159    #[test]
160    fn test_idempotence() {
161        // Test that calling with_version multiple times yields consistent results
162        let original_bytes = [0xAA; 16];
163        let uuid = create_test_uuid(original_bytes);
164
165        let uuid_v1 = uuid.with_version(1);
166        let uuid_v1_again = uuid_v1.with_version(1);
167        assert_eq!(
168            uuid_v1.bytes, uuid_v1_again.bytes,
169            "Repeated calls with same version should be idempotent"
170        );
171        assert_eq!(
172            uuid_v1_again.get_version(),
173            Some(1),
174            "Version should remain 1"
175        );
176
177        let uuid_v2 = uuid_v1.with_version(2);
178        assert_eq!(uuid_v2.get_version(), Some(2), "Version should change to 2");
179        assert!(has_osf_variant(&uuid_v2), "OSF variant should persist");
180    }
181
182    #[test]
183    fn test_version_change_preserves_other_fields() {
184        // Test changing version preserves all fields except version and variant
185        let original_bytes = [
186            0x11, 0x22, 0x33, 0x44, // time_low
187            0x55, 0x66, // time_mid
188            0x7A, 0x88, // time_hi_and_version (0x7A), clock_seq
189            0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00, // node
190        ];
191        let uuid = create_test_uuid(original_bytes);
192
193        let modified_uuid = uuid.with_version(4);
194        assert_eq!(
195            modified_uuid.get_version(),
196            Some(4),
197            "Version should be set to 4"
198        );
199        // Check all bytes except 6 (version) and 8 (variant)
200        let unchanged_indices = [0, 1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 13, 14, 15];
201        for i in &unchanged_indices {
202            assert_eq!(
203                modified_uuid.bytes[*i], original_bytes[*i],
204                "Byte {i} should remain unchanged"
205            );
206        }
207        // Check byte 6 preserves lower nibble (0xA from 0x7A)
208        assert_eq!(modified_uuid.bytes[6], 0x4A, "Byte 6 should be 0x4A");
209        assert!(has_osf_variant(&modified_uuid), "OSF variant not set");
210    }
211
212    #[test]
213    fn test_version_after_variant_change() {
214        // Test that setting version after variant change still works
215        let original_bytes = [0x00; 16];
216        let uuid = create_test_uuid(original_bytes);
217
218        let uuid_with_version = uuid.with_version(5);
219        let uuid_with_version_again = uuid_with_version.with_version(3);
220        assert_eq!(
221            uuid_with_version_again.get_version(),
222            Some(3),
223            "Version should be updated to 3"
224        );
225        assert!(
226            has_osf_variant(&uuid_with_version_again),
227            "OSF variant should be set"
228        );
229        assert_eq!(
230            uuid_with_version_again.bytes[6], 0x30,
231            "Byte 6 should reflect version 3 with zero lower nibble"
232        );
233    }
234}