| // Copyright 2023 Google LLC | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | /// A Uuid as defined by the Core Specification (v5.4, Vol 3, Part B, Sec 2.5.1) | 
 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 
 | pub struct Uuid(uuid::Uuid); | 
 |  | 
 | impl Uuid { | 
 |     // Non-changing parts of the Bluetooth Base UUID, for easy comparison and | 
 |     // construction. | 
 |     const BASE_UUID_B_PART: u16 = 0x0000; | 
 |     const BASE_UUID_C_PART: u16 = 0x1000; | 
 |     const BASE_UUID_D_PART: [u8; 8] = [0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB]; | 
 |  | 
 |     pub const fn from_u16(value: u16) -> Self { | 
 |         Self::from_u32(value as u32) | 
 |     } | 
 |  | 
 |     pub const fn from_u32(value: u32) -> Self { | 
 |         Uuid(uuid::Uuid::from_fields( | 
 |             value, | 
 |             Self::BASE_UUID_B_PART, | 
 |             Self::BASE_UUID_C_PART, | 
 |             &Self::BASE_UUID_D_PART, | 
 |         )) | 
 |     } | 
 |  | 
 |     pub fn to_u16(&self) -> Option<u16> { | 
 |         let x: u32 = self.to_u32()?; | 
 |         x.try_into().ok() | 
 |     } | 
 |  | 
 |     pub fn to_u32(&self) -> Option<u32> { | 
 |         let (first, second, third, final_bytes) = self.0.as_fields(); | 
 |         if second != Uuid::BASE_UUID_B_PART | 
 |             || third != Uuid::BASE_UUID_C_PART | 
 |             || final_bytes != &Uuid::BASE_UUID_D_PART | 
 |         { | 
 |             return None; | 
 |         } | 
 |         Some(first) | 
 |     } | 
 |  | 
 |     pub fn recognize(self) -> RecognizedUuid { | 
 |         self.into() | 
 |     } | 
 | } | 
 |  | 
 | impl From<Uuid> for uuid::Uuid { | 
 |     fn from(value: Uuid) -> Self { | 
 |         value.0 | 
 |     } | 
 | } | 
 |  | 
 | impl From<uuid::Uuid> for Uuid { | 
 |     fn from(value: uuid::Uuid) -> Self { | 
 |         Uuid(value) | 
 |     } | 
 | } | 
 |  | 
 | impl core::fmt::Display for Uuid { | 
 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
 |         if let Some(u) = self.to_u16() { | 
 |             return write!(f, "{u:#x}"); | 
 |         } | 
 |         if let Some(u) = self.to_u32() { | 
 |             return write!(f, "{u:#x}"); | 
 |         } | 
 |         write!(f, "{}", self.0.as_hyphenated()) | 
 |     } | 
 | } | 
 |  | 
 | impl core::str::FromStr for Uuid { | 
 |     type Err = uuid::Error; | 
 |  | 
 |     fn from_str(s: &str) -> core::result::Result<Uuid, Self::Err> { | 
 |         uuid::Uuid::parse_str(s).map(|uuid| Uuid(uuid)) | 
 |     } | 
 | } | 
 |  | 
 | #[derive(Clone, Debug)] | 
 | pub struct AssignedUuid { | 
 |     pub uuid: Uuid, | 
 |     pub name: String, | 
 |     pub id: Option<String>, | 
 | } | 
 |  | 
 | #[derive(Clone, Debug)] | 
 | pub enum RecognizedUuid { | 
 |     Assigned(AssignedUuid), | 
 |     Unrecognized(Uuid), | 
 | } | 
 |  | 
 | impl RecognizedUuid { | 
 |     fn as_uuid(&self) -> &Uuid { | 
 |         match self { | 
 |             RecognizedUuid::Assigned(AssignedUuid { uuid, .. }) => uuid, | 
 |             RecognizedUuid::Unrecognized(uuid) => uuid, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | impl From<Uuid> for RecognizedUuid { | 
 |     fn from(value: Uuid) -> Self { | 
 |         if let Some(assigned) = characteristic_uuids::CHARACTERISTIC_UUIDS.get(&value) { | 
 |             return Self::Assigned(assigned.clone()); | 
 |         } | 
 |         if let Some(assigned) = service_class::SERVICE_CLASS_UUIDS.get(&value) { | 
 |             return Self::Assigned(assigned.clone()); | 
 |         } | 
 |         if let Some(assigned) = service_uuids::SERVICE_UUIDS.get(&value) { | 
 |             return Self::Assigned(assigned.clone()); | 
 |         } | 
 |         Self::Unrecognized(value) | 
 |     } | 
 | } | 
 |  | 
 | impl std::ops::Deref for RecognizedUuid { | 
 |     type Target = Uuid; | 
 |  | 
 |     fn deref(&self) -> &Self::Target { | 
 |         self.as_uuid() | 
 |     } | 
 | } | 
 |  | 
 | impl std::ops::DerefMut for RecognizedUuid { | 
 |     fn deref_mut(&mut self) -> &mut Self::Target { | 
 |         match self { | 
 |             RecognizedUuid::Assigned(AssignedUuid { uuid, .. }) => uuid, | 
 |             RecognizedUuid::Unrecognized(uuid) => uuid, | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | impl std::fmt::Display for RecognizedUuid { | 
 |     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | 
 |         write!(f, "{}", self.as_uuid())?; | 
 |         if let RecognizedUuid::Assigned(AssignedUuid { name, .. }) = self { | 
 |             write!(f, " ({name})")?; | 
 |         } | 
 |         Ok(()) | 
 |     } | 
 | } | 
 |  | 
 | /// Constructs an AssignedUuid from a 16-bit UUID and a name, and an optional id | 
 | /// string. | 
 | macro_rules! assigned_uuid { | 
 |     ($uuid:expr, $name:expr) => { | 
 |         AssignedUuid { uuid: Uuid::from_u16($uuid), name: String::from($name), id: None } | 
 |     }; | 
 |     ($uuid:expr, $name:expr, $id:expr) => { | 
 |         AssignedUuid { | 
 |             uuid: Uuid::from_u16($uuid), | 
 |             name: String::from($name), | 
 |             id: Some(String::from($id)), | 
 |         } | 
 |     }; | 
 | } | 
 |  | 
 | macro_rules! assigned_uuid_map { | 
 |     ( $(($uuid:expr, $name:expr, $id:expr)),* $(,)? ) => { | 
 |         { | 
 |             let mut new_map = std::collections::HashMap::new(); | 
 |             $( | 
 |                 let _ = new_map.insert(Uuid::from_u16($uuid), assigned_uuid!($uuid, $name, $id)); | 
 |             )* | 
 |             new_map | 
 |         } | 
 |     }; | 
 |     ($(($uuid:expr, $name:expr)),* $(,)? ) => { | 
 |         { | 
 |             let mut new_map = std::collections::HashMap::new(); | 
 |             $( | 
 |                 let _ = new_map.insert(Uuid::from_u16($uuid), assigned_uuid!($uuid, $name)); | 
 |             )* | 
 |             new_map | 
 |         } | 
 |     }; | 
 | } | 
 |  | 
 | pub mod characteristic_uuids; | 
 | pub mod descriptors; | 
 | pub mod service_class; | 
 | pub mod service_uuids; | 
 |  | 
 | #[cfg(test)] | 
 | mod tests { | 
 |     use super::*; | 
 |  | 
 |     #[test] | 
 |     fn uuid16() { | 
 |         let uuid = Uuid::from_u16(0x180d); | 
 |         assert_eq!( | 
 |             uuid::Uuid::parse_str("0000180d-0000-1000-8000-00805f9b34fb").unwrap(), | 
 |             uuid.into() | 
 |         ); | 
 |         // Should shorten the UUID16s | 
 |         assert_eq!("0x180d", uuid.to_string()); | 
 |         let as16: u16 = uuid.to_u16().unwrap(); | 
 |         assert_eq!(0x180d, as16); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn uuid32() { | 
 |         let uuid = Uuid::from_u32(0xC0DECAFE); | 
 |         assert_eq!( | 
 |             uuid::Uuid::parse_str("c0decafe-0000-1000-8000-00805f9b34fb").unwrap(), | 
 |             uuid.into() | 
 |         ); | 
 |         // Should shorten the UUID16s | 
 |         assert_eq!("0xc0decafe", uuid.to_string()); | 
 |         assert!(uuid.to_u16().is_none()); | 
 |         let as32: u32 = uuid.to_u32().unwrap(); | 
 |         assert_eq!(0xc0decafe, as32); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn recognition() { | 
 |         // This is the "Running Speed and Cadence" service. | 
 |         let uuid = Uuid::from_u16(0x1814); | 
 |         assert!(uuid.recognize().to_string().contains("Cadence")); | 
 |         assert!( | 
 |             uuid.recognize().to_string().contains("0x1814"), | 
 |             "{} should contain the shortened uuid", | 
 |             uuid.to_string() | 
 |         ); | 
 |         // The "Wind Chill" characteristic. | 
 |         let uuid = Uuid::from_u16(0x2A79).recognize(); | 
 |         assert!(uuid.to_string().contains("Wind Chill")); | 
 |         assert!( | 
 |             uuid.to_string().contains("0x2a79"), | 
 |             "{} should contain the shortened uuid", | 
 |             uuid.to_string() | 
 |         ); | 
 |         // The "VideoSource" service class uuid | 
 |         let uuid = Uuid::from_u16(0x1303).recognize(); | 
 |         assert!(uuid.to_string().contains("VideoSource")); | 
 |         assert!( | 
 |             uuid.to_string().contains("0x1303"), | 
 |             "{} should contain the shortened uuid", | 
 |             uuid.to_string() | 
 |         ); | 
 |     } | 
 |  | 
 |     #[test] | 
 |     fn assigned_uuid_map() { | 
 |         // Assigned UUID info does not need to be unique | 
 |         let test_map = | 
 |             assigned_uuid_map!((0x1234, "Test", "org.example"), (0x5678, "Test", "org.example"),); | 
 |         assert!(test_map.contains_key(&Uuid::from_u16(0x1234))); | 
 |         assert!(test_map.contains_key(&Uuid::from_u16(0x5678))); | 
 |         assert!(!test_map.contains_key(&Uuid::from_u16(0x9ABC))); | 
 |  | 
 |         // Assigning the same UUID twice favors the second one. | 
 |         let test_map = | 
 |             assigned_uuid_map!((0x1234, "Test", "org.example"), (0x1234, "Test 2", "com.example"),); | 
 |  | 
 |         assert_eq!(test_map.get(&Uuid::from_u16(0x1234)).unwrap().name, "Test 2"); | 
 |     } | 
 | } |