use crate::error::WSError;
use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DeviceIdentity {
id: String,
device_type: Option<String>,
hardware_revision: Option<String>,
firmware_version: Option<String>,
}
impl DeviceIdentity {
pub fn new(id: impl Into<String>) -> Self {
Self {
id: id.into(),
device_type: None,
hardware_revision: None,
firmware_version: None,
}
}
pub fn from_mac(mac: &[u8]) -> Result<Self, WSError> {
if mac.len() != 6 {
return Err(WSError::InvalidArgument);
}
let mac_str = format!(
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
);
Ok(Self::new(mac_str))
}
pub fn from_uuid(uuid: &[u8]) -> Result<Self, WSError> {
if uuid.len() != 16 {
return Err(WSError::InvalidArgument);
}
let uuid_str = format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0],
uuid[1],
uuid[2],
uuid[3],
uuid[4],
uuid[5],
uuid[6],
uuid[7],
uuid[8],
uuid[9],
uuid[10],
uuid[11],
uuid[12],
uuid[13],
uuid[14],
uuid[15]
);
Ok(Self::new(uuid_str))
}
pub fn id(&self) -> &str {
&self.id
}
pub fn with_device_type(mut self, device_type: impl Into<String>) -> Self {
self.device_type = Some(device_type.into());
self
}
pub fn with_hardware_revision(mut self, revision: impl Into<String>) -> Self {
self.hardware_revision = Some(revision.into());
self
}
pub fn with_firmware_version(mut self, version: impl Into<String>) -> Self {
self.firmware_version = Some(version.into());
self
}
pub fn device_type(&self) -> Option<&str> {
self.device_type.as_deref()
}
pub fn hardware_revision(&self) -> Option<&str> {
self.hardware_revision.as_deref()
}
pub fn firmware_version(&self) -> Option<&str> {
self.firmware_version.as_deref()
}
pub fn validate(&self) -> Result<(), WSError> {
if self.id.is_empty() {
return Err(WSError::InvalidArgument);
}
if self.id.len() > 64 {
return Err(WSError::InvalidArgument);
}
let is_safe = self
.id
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '_' || c == ':' || c == '.');
if !is_safe {
return Err(WSError::InvalidArgument);
}
Ok(())
}
pub fn to_common_name(&self) -> String {
format!("Device {}", self.id)
}
pub fn to_description(&self) -> String {
let mut desc = format!("Device ID: {}", self.id);
if let Some(device_type) = &self.device_type {
desc.push_str(&format!(", Type: {}", device_type));
}
if let Some(hw_rev) = &self.hardware_revision {
desc.push_str(&format!(", HW Rev: {}", hw_rev));
}
if let Some(fw_ver) = &self.firmware_version {
desc.push_str(&format!(", FW Ver: {}", fw_ver));
}
desc
}
}
impl fmt::Display for DeviceIdentity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_device_identity_creation() {
let device = DeviceIdentity::new("device-123");
assert_eq!(device.id(), "device-123");
assert_eq!(device.to_string(), "device-123");
}
#[test]
fn test_device_identity_with_metadata() {
let device = DeviceIdentity::new("device-123")
.with_device_type("TemperatureSensor")
.with_hardware_revision("1.2")
.with_firmware_version("2.0.1");
assert_eq!(device.id(), "device-123");
assert_eq!(device.device_type(), Some("TemperatureSensor"));
assert_eq!(device.hardware_revision(), Some("1.2"));
assert_eq!(device.firmware_version(), Some("2.0.1"));
}
#[test]
fn test_from_mac() {
let mac = [0x00, 0x1A, 0x2B, 0x3C, 0x4D, 0x5E];
let device = DeviceIdentity::from_mac(&mac).unwrap();
assert_eq!(device.id(), "00:1A:2B:3C:4D:5E");
}
#[test]
fn test_from_mac_invalid_length() {
let mac = [0x00, 0x1A, 0x2B]; let result = DeviceIdentity::from_mac(&mac);
assert!(result.is_err());
}
#[test]
fn test_from_uuid() {
let uuid = [
0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
0x00, 0x00,
];
let device = DeviceIdentity::from_uuid(&uuid).unwrap();
assert_eq!(device.id(), "550e8400-e29b-41d4-a716-446655440000");
}
#[test]
fn test_from_uuid_invalid_length() {
let uuid = [0x55, 0x0e, 0x84]; let result = DeviceIdentity::from_uuid(&uuid);
assert!(result.is_err());
}
#[test]
fn test_validate_good_ids() {
assert!(DeviceIdentity::new("device-123").validate().is_ok());
assert!(DeviceIdentity::new("SN-2024-001234").validate().is_ok());
assert!(DeviceIdentity::new("00:1A:2B:3C:4D:5E").validate().is_ok());
assert!(DeviceIdentity::new("factory_A_line_3").validate().is_ok());
assert!(DeviceIdentity::new("device.123").validate().is_ok());
}
#[test]
fn test_validate_bad_ids() {
assert!(DeviceIdentity::new("").validate().is_err());
let long_id = "a".repeat(65);
assert!(DeviceIdentity::new(long_id).validate().is_err());
assert!(DeviceIdentity::new("device@123").validate().is_err());
assert!(DeviceIdentity::new("device#123").validate().is_err());
assert!(DeviceIdentity::new("device 123").validate().is_err()); }
#[test]
fn test_to_common_name() {
let device = DeviceIdentity::new("device-123");
assert_eq!(device.to_common_name(), "Device device-123");
}
#[test]
fn test_to_description() {
let device = DeviceIdentity::new("device-123")
.with_device_type("Sensor")
.with_hardware_revision("1.0")
.with_firmware_version("2.1.0");
let desc = device.to_description();
assert!(desc.contains("Device ID: device-123"));
assert!(desc.contains("Type: Sensor"));
assert!(desc.contains("HW Rev: 1.0"));
assert!(desc.contains("FW Ver: 2.1.0"));
}
#[test]
fn test_device_identity_equality() {
let device1 = DeviceIdentity::new("device-123");
let device2 = DeviceIdentity::new("device-123");
let device3 = DeviceIdentity::new("device-456");
assert_eq!(device1, device2);
assert_ne!(device1, device3);
}
}