use super::*;
#[repr(C)]
#[derive(Eq, PartialEq, Clone)]
pub struct GUID {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [ u8; 8 ],
}
impl std::fmt::Debug for GUID {
fn fmt( &self, f: &mut std::fmt::Formatter ) -> Result<(), std::fmt::Error> {
write!( f, "{}", self )
}
}
impl GUID {
pub fn zero_guid() -> GUID {
GUID { data1: 0, data2: 0, data3: 0, data4: [ 0; 8 ] }
}
pub fn parse( guid : &str ) -> Result< GUID, String >
{
enum GuidFormat { Braces, Hyphens, Raw }
let guid_format = match guid.len() {
38 => GuidFormat::Braces,
36 => GuidFormat::Hyphens,
32 => GuidFormat::Raw,
_ => return Err( format!(
"Unrecognized GUID format: '{}' ({})",
guid, guid.len() ) ),
};
let format = match guid_format {
GuidFormat::Braces => vec![
Some( b'{' ), None, None, None, None, None, None, None, None,
Some( b'-' ), None, None, None, None,
Some( b'-' ), None, None, None, None,
Some( b'-' ), None, None, None, None,
Some( b'-' ), None, None, None, None, None, None, None, None, None, None, None, None,
Some( b'}' )
],
GuidFormat::Hyphens => vec![
None, None, None, None, None, None, None, None,
Some( b'-' ), None, None, None, None,
Some( b'-' ), None, None, None, None,
Some( b'-' ), None, None, None, None,
Some( b'-' ), None, None, None, None, None, None, None, None, None, None, None, None
],
GuidFormat::Raw => vec![
None, None, None, None, None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None
]
};
let mut buffer = [ 0u8; 16 ];
let mut digit = 0;
for ( i_char, chr ) in guid.bytes().enumerate() {
if let Some( b ) = format[ i_char ] {
if chr == b {
continue
} else {
return Err( format!( "Unexpected character in GUID: {}", chr ) );
}
}
let value : u8 = match chr {
b'0'..= b'9' => chr - b'0',
b'a'..= b'f' => chr - b'a' + 10,
b'A'..= b'F' => chr - b'A' + 10,
_ => return Err( format!( "Unrecognized character in GUID: {}", chr ) )
};
let half = digit % 2;
let byte = ( digit - half ) / 2;
if half == 0 {
buffer[ byte ] += value * 16;
} else {
buffer[ byte ] += value;
}
digit += 1;
}
Ok( GUID {
data1:
( u32::from( buffer[ 0 ] ) << 24 ) +
( u32::from( buffer[ 1 ] ) << 16 ) +
( u32::from( buffer[ 2 ] ) << 8 ) +
( u32::from( buffer[ 3 ] ) ),
data2:
( u16::from( buffer[ 4 ] ) << 8 ) +
( u16::from( buffer[ 5 ] ) ),
data3:
( u16::from( buffer[ 6 ] ) << 8 ) +
( u16::from( buffer[ 7 ] ) ),
data4: [
buffer[ 8 ], buffer[ 9 ], buffer[ 10 ], buffer[ 11 ],
buffer[ 12 ], buffer[ 13 ], buffer[ 14 ], buffer[ 15 ],
]
} )
}
pub fn as_bytes( &self ) -> &[u8; 16]
{
unsafe {
&*( self as *const _ as *const [u8; 16] )
}
}
}
impl Default for GUID {
fn default() -> GUID {
GUID::zero_guid()
}
}
impl std::fmt::Display for GUID {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
fmt_guid( self, f, &GuidFmtCase::Upper, &GuidFmt::Braces )
}
}
impl std::fmt::LowerHex for GUID {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let fmt = if f.sign_minus() {
GuidFmt::Hyphens
} else {
GuidFmt::Raw
};
fmt_guid( self, f, &GuidFmtCase::Lower, &fmt )
}
}
impl std::fmt::UpperHex for GUID {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let fmt = if f.sign_minus() {
GuidFmt::Hyphens
} else {
GuidFmt::Raw
};
fmt_guid( self, f, &GuidFmtCase::Upper, &fmt )
}
}
enum GuidFmtCase { Lower, Upper }
enum GuidFmt { Braces, Hyphens, Raw }
fn fmt_guid(
g : &GUID,
f: &mut std::fmt::Formatter,
case : &GuidFmtCase,
fmt : &GuidFmt
) -> std::fmt::Result
{
match *case {
GuidFmtCase::Lower => match *fmt {
GuidFmt::Braces =>
write!( f,
"{{{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}}}",
g.data1, g.data2, g.data3,
g.data4[0], g.data4[1], g.data4[2], g.data4[3],
g.data4[4], g.data4[5], g.data4[6], g.data4[7] ),
GuidFmt::Hyphens =>
write!( f,
"{:08x}-{:04x}-{:04x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
g.data1, g.data2, g.data3,
g.data4[0], g.data4[1], g.data4[2], g.data4[3],
g.data4[4], g.data4[5], g.data4[6], g.data4[7] ),
GuidFmt::Raw =>
write!( f,
"{:08x}{:04x}{:04x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
g.data1, g.data2, g.data3,
g.data4[0], g.data4[1], g.data4[2], g.data4[3],
g.data4[4], g.data4[5], g.data4[6], g.data4[7] ),
},
GuidFmtCase::Upper => match *fmt {
GuidFmt::Braces =>
write!( f,
"{{{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}}}",
g.data1, g.data2, g.data3,
g.data4[0], g.data4[1], g.data4[2], g.data4[3],
g.data4[4], g.data4[5], g.data4[6], g.data4[7] ),
GuidFmt::Hyphens =>
write!( f,
"{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
g.data1, g.data2, g.data3,
g.data4[0], g.data4[1], g.data4[2], g.data4[3],
g.data4[4], g.data4[5], g.data4[6], g.data4[7] ),
GuidFmt::Raw =>
write!( f,
"{:08X}{:04X}{:04X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}",
g.data1, g.data2, g.data3,
g.data4[0], g.data4[1], g.data4[2], g.data4[3],
g.data4[4], g.data4[5], g.data4[6], g.data4[7] ),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn zero_guid() {
let guid = GUID::zero_guid();
assert_eq!( 0, guid.data1 );
assert_eq!( 0, guid.data2 );
assert_eq!( 0, guid.data3 );
assert_eq!( [ 0, 0, 0, 0, 0, 0, 0, 0 ], guid.data4 );
}
#[test]
fn parse_braces() {
let expected = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
let actual = GUID::parse( "{12345678-90ab-cdef-fedc-ba0987654321}" )
.unwrap();
assert_eq!( expected, actual );
}
#[test]
fn parse_hyphenated() {
let expected = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
let actual = GUID::parse( "12345678-90ab-cdef-fedc-ba0987654321" )
.unwrap();
assert_eq!( expected, actual );
}
#[test]
fn parse_raw() {
let expected = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
let actual = GUID::parse( "1234567890abcdeffedcba0987654321" )
.unwrap();
assert_eq!( expected, actual );
}
#[test]
fn format_default() {
let expected = "{12345678-90AB-CDEF-FEDC-BA0987654321}";
let guid = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
assert_eq!( expected, format!( "{}", guid ) );
}
#[test]
fn format_lowerhex() {
let expected = "1234567890abcdeffedcba0987654321";
let guid = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
assert_eq!( expected, format!( "{:x}", guid ) );
}
#[test]
fn format_lowerhex_hyphens() {
let expected = "12345678-90ab-cdef-fedc-ba0987654321";
let guid = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
assert_eq!( expected, format!( "{:-x}", guid ) );
}
#[test]
fn format_upperhex() {
let expected = "1234567890ABCDEFFEDCBA0987654321";
let guid = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
assert_eq!( expected, format!( "{:X}", guid ) );
}
#[test]
fn format_upperhex_hyphens() {
let expected = "12345678-90AB-CDEF-FEDC-BA0987654321";
let guid = GUID {
data1: 0x12345678,
data2: 0x90ab,
data3: 0xcdef,
data4: [ 0xfe, 0xdc, 0xba, 0x09, 0x87, 0x65, 0x43, 0x21 ]
};
assert_eq!( expected, format!( "{:-X}", guid ) );
}
}