1use std::fmt;
2
3#[derive(Clone, Copy, PartialEq, Eq)]
7pub struct Crc32c(u32);
8
9impl Crc32c {
10 #[must_use]
12 pub const fn from_raw(value: u32) -> Self {
13 Self(value)
14 }
15
16 #[must_use]
18 pub const fn value(&self) -> u32 {
19 self.0
20 }
21}
22
23impl fmt::Debug for Crc32c {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 write!(f, "Crc32c({:#010x})", self.0)
26 }
27}
28
29impl fmt::Display for Crc32c {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "{:#010x}", self.0)
32 }
33}
34
35#[derive(Clone, Copy, PartialEq, Eq, Hash)]
40pub struct Guid {
41 bytes: [u8; 16],
42}
43
44impl Guid {
45 #[must_use]
47 pub const fn from_bytes(bytes: [u8; 16]) -> Self {
48 Self { bytes }
49 }
50
51 #[must_use]
53 pub const fn zero() -> Self {
54 Self { bytes: [0u8; 16] }
55 }
56
57 #[must_use]
59 pub const fn to_bytes(&self) -> [u8; 16] {
60 self.bytes
61 }
62
63 #[must_use]
65 pub fn new_v4() -> Self {
66 let uuid = uuid::Uuid::new_v4();
67 Self {
68 bytes: *uuid.as_bytes(),
69 }
70 }
71
72 #[must_use]
74 pub fn to_uuid(&self) -> uuid::Uuid {
75 uuid::Uuid::from_bytes(self.bytes)
76 }
77
78 pub fn parse_braced(input: &str) -> std::result::Result<Self, uuid::Error> {
84 let trimmed = input.trim();
85 let guid = trimmed
86 .strip_prefix('{')
87 .and_then(|value| value.strip_suffix('}'))
88 .unwrap_or(trimmed);
89 uuid::Uuid::parse_str(guid).map(|uuid| Self {
90 bytes: *uuid.as_bytes(),
91 })
92 }
93}
94
95impl fmt::Debug for Guid {
96 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97 write!(f, "Guid({})", self.to_uuid().hyphenated())
98 }
99}
100
101impl fmt::Display for Guid {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "{}", self.to_uuid().hyphenated())
104 }
105}
106
107#[allow(non_snake_case)]
109pub mod StandardItems {
110 use crate::constants;
111 use crate::types::Guid;
112
113 pub const FILE_PARAMETERS: Guid = constants::FILE_PARAMETERS_GUID;
114 pub const VIRTUAL_DISK_SIZE: Guid = constants::VIRTUAL_DISK_SIZE_GUID;
115 pub const VIRTUAL_DISK_ID: Guid = constants::VIRTUAL_DISK_ID_GUID;
116 pub const LOGICAL_SECTOR_SIZE: Guid = constants::LOGICAL_SECTOR_SIZE_GUID;
117 pub const PHYSICAL_SECTOR_SIZE: Guid = constants::PHYSICAL_SECTOR_SIZE_GUID;
118 pub const PARENT_LOCATOR: Guid = constants::PARENT_LOCATOR_GUID;
119 pub const LOCATOR_TYPE_VHDX: Guid = constants::LOCATOR_TYPE_VHDX_GUID;
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125
126 #[test]
127 fn guid_from_bytes_roundtrip() {
128 let bytes = [
129 0x37, 0x67, 0xA1, 0xCA, 0x36, 0xFA, 0x43, 0x4D, 0xB3, 0xB6, 0x33, 0xF0, 0xAA, 0x44,
130 0xE7, 0x6B,
131 ];
132 let guid = Guid::from_bytes(bytes);
133 assert_eq!(guid.to_bytes(), bytes);
134 }
135
136 #[test]
137 fn guid_display() {
138 let guid = StandardItems::FILE_PARAMETERS;
139 let displayed = format!("{guid}");
141 assert_eq!(displayed, "3767a1ca-36fa-434d-b3b6-33f0aa44e76b");
142 }
143
144 #[test]
145 fn guid_debug() {
146 let guid = StandardItems::FILE_PARAMETERS;
147 let debugged = format!("{guid:?}");
148 assert!(debugged.starts_with("Guid("));
149 assert!(debugged.ends_with(')'));
150 }
151
152 #[test]
153 fn guid_new_v4_is_unique() {
154 let a = Guid::new_v4();
155 let b = Guid::new_v4();
156 assert_ne!(a, b);
157 }
158
159 #[test]
160 fn standard_items_are_distinct() {
161 let guids = [
162 StandardItems::FILE_PARAMETERS,
163 StandardItems::VIRTUAL_DISK_SIZE,
164 StandardItems::VIRTUAL_DISK_ID,
165 StandardItems::LOGICAL_SECTOR_SIZE,
166 StandardItems::PHYSICAL_SECTOR_SIZE,
167 StandardItems::PARENT_LOCATOR,
168 StandardItems::LOCATOR_TYPE_VHDX,
169 ];
170 for (i, a) in guids.iter().enumerate() {
171 for (j, b) in guids.iter().enumerate() {
172 if i != j {
173 assert_ne!(a, b, "StandardItems at index {i} and {j} should differ");
174 }
175 }
176 }
177 }
178}