1use serde::{Deserialize, Serialize};
10use std::str::FromStr;
11use {fidl_fuchsia_bluetooth as fidl, fidl_fuchsia_bluetooth_bredr as fidlbredr};
12
13use crate::error::Error;
14use crate::inspect::ToProperty;
15
16#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
17pub struct Uuid(uuid::Uuid);
18
19#[derive(Copy, Clone, Debug, PartialEq)]
20pub struct U64Pair {
21 pub least_significant_bits: u64,
22 pub most_significant_bits: u64,
23}
24
25const BASE_UUID_FINAL_EIGHT_BYTES: [u8; 8] = [0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB];
27
28impl Uuid {
29 pub const BLUETOOTH_UUID_LENGTH_BYTES: usize = 16;
31
32 pub const fn from_bytes(bytes_little_endian: uuid::Bytes) -> Uuid {
34 let u = u128::from_le_bytes(bytes_little_endian);
35 Uuid(uuid::Uuid::from_u128(u))
36 }
37
38 pub const fn from_be_bytes(bytes_big_endian: uuid::Bytes) -> Uuid {
40 let u = u128::from_be_bytes(bytes_big_endian);
41 Uuid(uuid::Uuid::from_u128(u))
42 }
43
44 pub fn as_be_bytes(&self) -> &[u8; Self::BLUETOOTH_UUID_LENGTH_BYTES] {
45 self.0.as_bytes()
47 }
48
49 pub fn to_u64_pair(&self) -> U64Pair {
50 let (msbs, lsbs) = self.0.as_u64_pair();
51 U64Pair { least_significant_bits: lsbs, most_significant_bits: msbs }
52 }
53
54 pub fn from_u64_pair(u64_pair: U64Pair) -> Self {
56 let inner = uuid::Uuid::from_u64_pair(
57 u64_pair.most_significant_bits,
58 u64_pair.least_significant_bits,
59 );
60 Self(inner)
61 }
62
63 pub const fn new16(value: u16) -> Uuid {
64 Uuid::new32(value as u32)
65 }
66
67 pub const fn new32(value: u32) -> Uuid {
68 Uuid(uuid::Uuid::from_fields(value, 0x0000, 0x1000, &BASE_UUID_FINAL_EIGHT_BYTES))
71 }
72
73 pub fn to_string(&self) -> String {
74 self.0.as_hyphenated().to_string()
75 }
76}
77
78impl TryFrom<Uuid> for u32 {
79 type Error = Error;
80
81 fn try_from(u: Uuid) -> Result<u32, <u32 as TryFrom<Uuid>>::Error> {
82 let (first, second, third, final_bytes) = u.0.as_fields();
83 if second != 0x0000 || third != 0x1000 || final_bytes != &BASE_UUID_FINAL_EIGHT_BYTES {
84 return Err(Error::conversion("not derived from the base UUID"));
85 }
86 Ok(first)
87 }
88}
89
90impl TryFrom<Uuid> for u16 {
91 type Error = Error;
92
93 fn try_from(u: Uuid) -> Result<u16, <u16 as TryFrom<Uuid>>::Error> {
94 let x: u32 = u.try_into()?;
95 x.try_into().map_err(|_e| Error::conversion("not a 16-bit UUID"))
96 }
97}
98
99impl From<&fidl::Uuid> for Uuid {
100 fn from(src: &fidl::Uuid) -> Uuid {
101 Uuid::from_bytes(src.value)
102 }
103}
104
105impl From<fidl::Uuid> for Uuid {
106 fn from(src: fidl::Uuid) -> Uuid {
107 Uuid::from(&src)
108 }
109}
110
111impl From<&Uuid> for fidl::Uuid {
112 fn from(src: &Uuid) -> fidl::Uuid {
113 let mut bytes = src.0.as_bytes().clone();
114 bytes.reverse();
115 fidl::Uuid { value: bytes }
116 }
117}
118
119impl From<Uuid> for fidl::Uuid {
120 fn from(src: Uuid) -> fidl::Uuid {
121 fidl::Uuid::from(&src)
122 }
123}
124
125impl From<uuid::Uuid> for Uuid {
126 fn from(src: uuid::Uuid) -> Uuid {
127 Uuid(src)
128 }
129}
130
131impl From<Uuid> for uuid::Uuid {
132 fn from(src: Uuid) -> uuid::Uuid {
133 src.0
134 }
135}
136
137impl TryFrom<Uuid> for fidlbredr::ServiceClassProfileIdentifier {
138 type Error = Error;
139
140 fn try_from(value: Uuid) -> Result<Self, Self::Error> {
141 let short: u16 = value.try_into()?;
142 Self::from_primitive(short).ok_or_else(|| {
143 Error::conversion(format!("unknown ServiceClassProfileIdentifier: {short}"))
144 })
145 }
146}
147
148impl From<fidlbredr::ServiceClassProfileIdentifier> for Uuid {
149 fn from(src: fidlbredr::ServiceClassProfileIdentifier) -> Self {
150 Uuid::new16(src.into_primitive())
151 }
152}
153
154impl From<Uuid> for fidlbredr::DataElement {
155 fn from(src: Uuid) -> Self {
156 fidlbredr::DataElement::Uuid(src.into())
157 }
158}
159
160impl FromStr for Uuid {
161 type Err = Error;
162
163 fn from_str(s: &str) -> Result<Uuid, Self::Err> {
164 uuid::Uuid::parse_str(s).map(|uuid| Uuid(uuid)).map_err(Error::external)
165 }
166}
167
168impl ToProperty for Uuid {
169 type PropertyType = String;
170 fn to_property(&self) -> Self::PropertyType {
171 self.to_string()
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use proptest::prelude::*;
179
180 #[test]
181 fn uuid16_to_string() {
182 let uuid = Uuid::new16(0x180d);
183 assert_eq!("0000180d-0000-1000-8000-00805f9b34fb", uuid.to_string());
184 }
185
186 #[test]
187 fn uuid32_to_string() {
188 let uuid = Uuid::new32(0xAABBCCDD);
189 assert_eq!("aabbccdd-0000-1000-8000-00805f9b34fb", uuid.to_string());
190 }
191
192 proptest! {
193 #[test]
194 fn all_uuid32_valid(n in prop::num::u32::ANY) {
195 let uuid = Uuid::new32(n);
198 let string = uuid.to_string();
199 assert_eq!("-0000-1000-8000-00805f9b34fb", &(string[8..]));
200 let back: u32 = uuid.try_into().expect("can to back to u32");
201 assert_eq!(back, n);
202 }
203 }
204
205 proptest! {
206 #[test]
207 fn all_uuid16_valid(n in prop::num::u16::ANY) {
208 let uuid = Uuid::new16(n);
211 let string = uuid.to_string();
212 assert_eq!("-0000-1000-8000-00805f9b34fb", &(string[8..]));
213 assert_eq!("00", &(string[0..2]));
214 let back: u16 = uuid.try_into().expect("can to back to u16");
215 assert_eq!(back, n);
216 }
217 }
218
219 proptest! {
220 #[test]
221 fn parser_roundtrip(n in prop::num::u32::ANY) {
222 let uuid = Uuid::new32(n);
223 let string = uuid.to_string();
224 let parsed = string.parse::<Uuid>();
225 assert_eq!(Ok(uuid), parsed.map_err(|e| format!("{:?}", e)));
226 }
227 }
228
229 #[test]
230 fn uuid128_to_string() {
231 let uuid = Uuid::from_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
232 assert_eq!("0f0e0d0c-0b0a-0908-0706-050403020100", uuid.to_string());
233 }
234
235 #[test]
236 fn uuid_from_fidl() {
237 let uuid = fidl::Uuid { value: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] };
238 let uuid: Uuid = uuid.into();
239 assert_eq!("0f0e0d0c-0b0a-0908-0706-050403020100", uuid.to_string());
240 }
241
242 #[test]
243 fn uuid_into_fidl() {
244 let uuid = Uuid::from_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
245 let uuid: fidl::Uuid = uuid.into();
246 let expected = fidl::Uuid { value: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] };
247 assert_eq!(expected, uuid);
248 }
249
250 #[test]
251 fn u64_pair_roundtrip() {
252 #[rustfmt::skip]
253 let bytes: [u8; 16] = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf];
255 let uuid = Uuid::from_bytes(bytes);
256 let u64_pair = uuid.to_u64_pair();
257
258 assert_eq!(0x0706050403020100, u64_pair.least_significant_bits);
261 assert_eq!(0x0f0e0d0c0b0a0908, u64_pair.most_significant_bits);
262
263 let result_uuid = Uuid::from_u64_pair(u64_pair);
264
265 assert_eq!(uuid, result_uuid);
266 }
267}