1use std::collections::HashMap;
6use std::error::Error;
7use std::sync::Arc;
8
9#[cfg(feature = "bluetooth-test")]
10use blurmock::fake_characteristic::FakeBluetoothGATTCharacteristic;
11#[cfg(feature = "bluetooth-test")]
12use blurmock::fake_descriptor::FakeBluetoothGATTDescriptor;
13#[cfg(feature = "bluetooth-test")]
14use blurmock::fake_device::FakeBluetoothDevice;
15#[cfg(feature = "bluetooth-test")]
16use blurmock::fake_discovery_session::FakeBluetoothDiscoverySession;
17#[cfg(feature = "bluetooth-test")]
18use blurmock::fake_service::FakeBluetoothGATTService;
19#[cfg(feature = "native-bluetooth")]
20use btleplug::api::{Central, CharPropFlags, Peripheral, ScanFilter, WriteType};
21#[cfg(feature = "native-bluetooth")]
22use btleplug::platform::{Adapter, Peripheral as PlatformPeripheral};
23
24pub use super::adapter::BluetoothAdapter;
25use crate::macros::get_inner_and_call_test_func;
26
27#[cfg(feature = "native-bluetooth")]
28#[derive(Clone, Debug)]
29pub struct BtleplugDiscoverySession {
30 pub(crate) adapter: Adapter,
31}
32
33#[cfg(feature = "native-bluetooth")]
34impl BtleplugDiscoverySession {
35 pub async fn start_discovery(&self) -> Result<(), Box<dyn Error>> {
36 Ok(self.adapter.start_scan(ScanFilter::default()).await?)
37 }
38
39 pub async fn stop_discovery(&self) -> Result<(), Box<dyn Error>> {
40 Ok(self.adapter.stop_scan().await?)
41 }
42}
43
44#[cfg(feature = "native-bluetooth")]
45#[derive(Clone, Debug)]
46pub struct BtleplugDevice {
47 pub(crate) peripheral: PlatformPeripheral,
48}
49
50#[cfg(feature = "native-bluetooth")]
51impl BtleplugDevice {
52 async fn properties(&self) -> Result<btleplug::api::PeripheralProperties, Box<dyn Error>> {
53 self.peripheral
54 .properties()
55 .await?
56 .ok_or_else(|| Box::from("Device properties not available"))
57 }
58
59 pub fn get_id(&self) -> String {
60 self.peripheral.id().to_string()
61 }
62
63 pub fn get_address(&self) -> Result<String, Box<dyn Error>> {
64 Ok(self.peripheral.id().to_string())
67 }
68
69 pub async fn get_name(&self) -> Result<String, Box<dyn Error>> {
70 let props = self.properties().await?;
71 if let Some(name) = props.local_name {
72 return Ok(name);
73 }
74 if let Some(name) = props.advertisement_name {
75 return Ok(name);
76 }
77 Err(Box::from("Device name not available"))
78 }
79
80 pub async fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
81 Ok(self
82 .properties()
83 .await?
84 .services
85 .into_iter()
86 .map(|uuid| uuid.to_string())
87 .collect())
88 }
89
90 pub async fn is_connected(&self) -> Result<bool, Box<dyn Error>> {
91 Ok(self.peripheral.is_connected().await.map_err(Box::new)?)
92 }
93
94 pub async fn connect(&self) -> Result<(), Box<dyn Error>> {
95 Ok(self.peripheral.connect().await.map_err(Box::new)?)
96 }
97
98 pub async fn disconnect(&self) -> Result<(), Box<dyn Error>> {
99 Ok(self.peripheral.disconnect().await.map_err(Box::new)?)
100 }
101
102 pub async fn get_manufacturer_data(&self) -> Result<HashMap<u16, Vec<u8>>, Box<dyn Error>> {
103 Ok(self.properties().await?.manufacturer_data)
104 }
105
106 pub async fn get_service_data(&self) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
107 Ok(self
108 .properties()
109 .await?
110 .service_data
111 .into_iter()
112 .map(|(uuid, data)| (uuid.to_string(), data))
113 .collect())
114 }
115
116 pub async fn discover_services(&self) -> Result<Vec<BluetoothGATTService>, Box<dyn Error>> {
117 self.peripheral
118 .discover_services()
119 .await
120 .map_err(Box::new)?;
121
122 let device_id = self.peripheral.id().to_string();
123 let services = self.peripheral.services();
124 let mut result = Vec::new();
125 let mut uuid_counts: HashMap<String, usize> = HashMap::new();
126
127 for service in services {
128 let uuid_str = service.uuid.to_string();
129 let idx = uuid_counts.entry(uuid_str.clone()).or_default();
130 let instance_id = format!("{device_id}/svc/{uuid_str}/{idx}");
131 *idx += 1;
132 result.push(BluetoothGATTService::Btleplug(BtleplugGATTService {
133 instance_id,
134 service,
135 peripheral: self.peripheral.clone(),
136 }));
137 }
138 Ok(result)
139 }
140}
141
142#[cfg(feature = "native-bluetooth")]
143#[derive(Clone, Debug)]
144pub struct BtleplugGATTService {
145 pub(crate) instance_id: String,
146 pub(crate) service: btleplug::api::Service,
147 pub(crate) peripheral: PlatformPeripheral,
148}
149
150#[cfg(feature = "native-bluetooth")]
151impl BtleplugGATTService {
152 pub fn get_id(&self) -> String {
153 self.instance_id.clone()
154 }
155
156 pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
157 Ok(self.service.uuid.to_string())
158 }
159
160 pub fn is_primary(&self) -> Result<bool, Box<dyn Error>> {
161 Ok(self.service.primary)
162 }
163
164 pub fn get_includes(&self) -> Result<Vec<String>, Box<dyn Error>> {
165 Ok(vec![])
168 }
169
170 pub fn get_gatt_characteristics(&self) -> Vec<BluetoothGATTCharacteristic> {
171 let mut result = Vec::new();
172 let mut uuid_counts: HashMap<String, usize> = HashMap::new();
173
174 for characteristic in &self.service.characteristics {
175 let uuid_str = characteristic.uuid.to_string();
176 let idx = uuid_counts.entry(uuid_str.clone()).or_default();
177 let instance_id = format!("{}/char/{uuid_str}/{idx}", self.instance_id);
178 *idx += 1;
179 result.push(BluetoothGATTCharacteristic::Btleplug(
180 BtleplugGATTCharacteristic {
181 instance_id,
182 characteristic: characteristic.clone(),
183 peripheral: self.peripheral.clone(),
184 },
185 ));
186 }
187 result
188 }
189}
190
191#[cfg(feature = "native-bluetooth")]
192#[derive(Clone, Debug)]
193pub struct BtleplugGATTCharacteristic {
194 pub(crate) instance_id: String,
195 pub(crate) characteristic: btleplug::api::Characteristic,
196 pub(crate) peripheral: PlatformPeripheral,
197}
198
199#[cfg(feature = "native-bluetooth")]
200impl BtleplugGATTCharacteristic {
201 pub fn get_id(&self) -> String {
202 self.instance_id.clone()
203 }
204
205 pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
206 Ok(self.characteristic.uuid.to_string())
207 }
208
209 pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
210 let props = self.characteristic.properties;
211 let mut flags = Vec::new();
212
213 if props.contains(CharPropFlags::BROADCAST) {
214 flags.push("broadcast".to_string());
215 }
216 if props.contains(CharPropFlags::READ) {
217 flags.push("read".to_string());
218 }
219 if props.contains(CharPropFlags::WRITE_WITHOUT_RESPONSE) {
220 flags.push("write-without-response".to_string());
221 }
222 if props.contains(CharPropFlags::WRITE) {
223 flags.push("write".to_string());
224 }
225 if props.contains(CharPropFlags::NOTIFY) {
226 flags.push("notify".to_string());
227 }
228 if props.contains(CharPropFlags::INDICATE) {
229 flags.push("indicate".to_string());
230 }
231 if props.contains(CharPropFlags::AUTHENTICATED_SIGNED_WRITES) {
232 flags.push("authenticated-signed-writes".to_string());
233 }
234 Ok(flags)
235 }
236
237 pub fn get_gatt_descriptors(&self) -> Vec<BluetoothGATTDescriptor> {
238 let mut result = Vec::new();
239 let mut uuid_counts: HashMap<String, usize> = HashMap::new();
240
241 for descriptor in &self.characteristic.descriptors {
242 let uuid_str = descriptor.uuid.to_string();
243 let idx = uuid_counts.entry(uuid_str.clone()).or_default();
244 let instance_id = format!("{}/desc/{uuid_str}/{idx}", self.instance_id);
245 *idx += 1;
246 result.push(BluetoothGATTDescriptor::Btleplug(BtleplugGATTDescriptor {
247 instance_id,
248 descriptor: descriptor.clone(),
249 peripheral: self.peripheral.clone(),
250 }));
251 }
252 result
253 }
254
255 pub async fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
256 Ok(self
257 .peripheral
258 .read(&self.characteristic)
259 .await
260 .map_err(Box::new)?)
261 }
262
263 pub async fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
264 let write_type = if self
265 .characteristic
266 .properties
267 .contains(CharPropFlags::WRITE)
268 {
269 WriteType::WithResponse
270 } else {
271 WriteType::WithoutResponse
272 };
273 Ok(self
274 .peripheral
275 .write(&self.characteristic, &values, write_type)
276 .await
277 .map_err(Box::new)?)
278 }
279
280 pub async fn start_notify(&self) -> Result<(), Box<dyn Error>> {
281 Ok(self
282 .peripheral
283 .subscribe(&self.characteristic)
284 .await
285 .map_err(Box::new)?)
286 }
287
288 pub async fn stop_notify(&self) -> Result<(), Box<dyn Error>> {
289 Ok(self
290 .peripheral
291 .unsubscribe(&self.characteristic)
292 .await
293 .map_err(Box::new)?)
294 }
295}
296
297#[cfg(feature = "native-bluetooth")]
298#[derive(Clone, Debug)]
299pub struct BtleplugGATTDescriptor {
300 pub(crate) instance_id: String,
301 pub(crate) descriptor: btleplug::api::Descriptor,
302 pub(crate) peripheral: PlatformPeripheral,
303}
304
305#[cfg(feature = "native-bluetooth")]
306impl BtleplugGATTDescriptor {
307 pub fn get_id(&self) -> String {
308 self.instance_id.clone()
309 }
310
311 pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
312 Ok(self.descriptor.uuid.to_string())
313 }
314
315 pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
316 Ok(vec![])
317 }
318
319 pub async fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
320 Ok(self
321 .peripheral
322 .read_descriptor(&self.descriptor)
323 .await
324 .map_err(Box::new)?)
325 }
326
327 pub async fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
328 Ok(self
329 .peripheral
330 .write_descriptor(&self.descriptor, &values)
331 .await
332 .map_err(Box::new)?)
333 }
334}
335
336#[derive(Debug)]
337pub enum BluetoothDiscoverySession {
338 #[cfg(feature = "native-bluetooth")]
339 Btleplug(BtleplugDiscoverySession),
340 #[cfg(feature = "bluetooth-test")]
341 Mock(Arc<FakeBluetoothDiscoverySession>),
342}
343
344impl BluetoothDiscoverySession {
345 pub async fn start_discovery(&self) -> Result<(), Box<dyn Error>> {
346 match self {
347 #[cfg(feature = "native-bluetooth")]
348 Self::Btleplug(inner) => inner.start_discovery().await,
349 #[cfg(feature = "bluetooth-test")]
350 Self::Mock(inner) => inner.start_discovery(),
351 }
352 }
353
354 pub async fn stop_discovery(&self) -> Result<(), Box<dyn Error>> {
355 match self {
356 #[cfg(feature = "native-bluetooth")]
357 Self::Btleplug(inner) => inner.stop_discovery().await,
358 #[cfg(feature = "bluetooth-test")]
359 Self::Mock(inner) => inner.stop_discovery(),
360 }
361 }
362}
363
364#[derive(Clone, Debug)]
365pub enum BluetoothDevice {
366 #[cfg(feature = "native-bluetooth")]
367 Btleplug(BtleplugDevice),
368 #[cfg(feature = "bluetooth-test")]
369 Mock(Arc<FakeBluetoothDevice>),
370}
371
372impl BluetoothDevice {
373 pub fn get_id(&self) -> String {
374 match self {
375 #[cfg(feature = "native-bluetooth")]
376 Self::Btleplug(inner) => inner.get_id(),
377 #[cfg(feature = "bluetooth-test")]
378 Self::Mock(inner) => inner.get_id(),
379 }
380 }
381
382 pub fn get_address(&self) -> Result<String, Box<dyn Error>> {
383 match self {
384 #[cfg(feature = "native-bluetooth")]
385 Self::Btleplug(inner) => inner.get_address(),
386 #[cfg(feature = "bluetooth-test")]
387 Self::Mock(inner) => inner.get_address(),
388 }
389 }
390
391 pub async fn get_name(&self) -> Result<String, Box<dyn Error>> {
392 match self {
393 #[cfg(feature = "native-bluetooth")]
394 Self::Btleplug(inner) => inner.get_name().await,
395 #[cfg(feature = "bluetooth-test")]
396 Self::Mock(inner) => inner.get_name(),
397 }
398 }
399
400 pub async fn get_uuids(&self) -> Result<Vec<String>, Box<dyn Error>> {
401 match self {
402 #[cfg(feature = "native-bluetooth")]
403 Self::Btleplug(inner) => inner.get_uuids().await,
404 #[cfg(feature = "bluetooth-test")]
405 Self::Mock(inner) => inner.get_uuids(),
406 }
407 }
408
409 pub async fn is_connected(&self) -> Result<bool, Box<dyn Error>> {
410 match self {
411 #[cfg(feature = "native-bluetooth")]
412 Self::Btleplug(inner) => inner.is_connected().await,
413 #[cfg(feature = "bluetooth-test")]
414 Self::Mock(inner) => inner.is_connected(),
415 }
416 }
417
418 pub async fn connect(&self) -> Result<(), Box<dyn Error>> {
419 match self {
420 #[cfg(feature = "native-bluetooth")]
421 Self::Btleplug(inner) => inner.connect().await,
422 #[cfg(feature = "bluetooth-test")]
423 Self::Mock(inner) => inner.connect(),
424 }
425 }
426
427 pub async fn disconnect(&self) -> Result<(), Box<dyn Error>> {
428 match self {
429 #[cfg(feature = "native-bluetooth")]
430 Self::Btleplug(inner) => inner.disconnect().await,
431 #[cfg(feature = "bluetooth-test")]
432 Self::Mock(inner) => inner.disconnect(),
433 }
434 }
435
436 pub async fn get_manufacturer_data(&self) -> Result<HashMap<u16, Vec<u8>>, Box<dyn Error>> {
437 match self {
438 #[cfg(feature = "native-bluetooth")]
439 Self::Btleplug(inner) => inner.get_manufacturer_data().await,
440 #[cfg(feature = "bluetooth-test")]
441 Self::Mock(inner) => inner.get_manufacturer_data(),
442 }
443 }
444
445 pub async fn get_service_data(&self) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
446 match self {
447 #[cfg(feature = "native-bluetooth")]
448 Self::Btleplug(inner) => inner.get_service_data().await,
449 #[cfg(feature = "bluetooth-test")]
450 Self::Mock(inner) => inner.get_service_data(),
451 }
452 }
453
454 pub async fn get_gatt_services(&self) -> Result<Vec<BluetoothGATTService>, Box<dyn Error>> {
455 match self {
456 #[cfg(feature = "native-bluetooth")]
457 Self::Btleplug(inner) => inner.discover_services().await,
458 #[cfg(feature = "bluetooth-test")]
459 Self::Mock(inner) => {
460 let services = inner.get_gatt_services()?;
461 Ok(services
462 .into_iter()
463 .map(|service| {
464 BluetoothGATTService::Mock(FakeBluetoothGATTService::new_empty(
465 inner.clone(),
466 service,
467 ))
468 })
469 .collect())
470 },
471 }
472 }
473
474 #[cfg(feature = "bluetooth-test")]
475 pub fn set_id(&self, id: String) {
476 #[allow(irrefutable_let_patterns)]
477 let Self::Mock(inner) = self else {
478 return;
479 };
480 inner.set_id(id);
481 }
482
483 #[cfg(feature = "bluetooth-test")]
484 pub fn set_address(&self, address: String) -> Result<(), Box<dyn Error>> {
485 get_inner_and_call_test_func!(self, BluetoothDevice, set_address, address)
486 }
487
488 #[cfg(feature = "bluetooth-test")]
489 pub fn set_name(&self, name: Option<String>) -> Result<(), Box<dyn Error>> {
490 get_inner_and_call_test_func!(self, BluetoothDevice, set_name, name)
491 }
492
493 #[cfg(feature = "bluetooth-test")]
494 pub fn set_uuids(&self, uuids: Vec<String>) -> Result<(), Box<dyn Error>> {
495 get_inner_and_call_test_func!(self, BluetoothDevice, set_uuids, uuids)
496 }
497
498 #[cfg(feature = "bluetooth-test")]
499 pub fn set_connectable(&self, connectable: bool) -> Result<(), Box<dyn Error>> {
500 get_inner_and_call_test_func!(self, BluetoothDevice, set_connectable, connectable)
501 }
502
503 #[cfg(feature = "bluetooth-test")]
504 pub fn set_connected(&self, connected: bool) -> Result<(), Box<dyn Error>> {
505 get_inner_and_call_test_func!(self, BluetoothDevice, set_connected, connected)
506 }
507
508 #[cfg(feature = "bluetooth-test")]
509 pub fn set_manufacturer_data(
510 &self,
511 manufacturer_data: HashMap<u16, Vec<u8>>,
512 ) -> Result<(), Box<dyn Error>> {
513 get_inner_and_call_test_func!(
514 self,
515 BluetoothDevice,
516 set_manufacturer_data,
517 Some(manufacturer_data)
518 )
519 }
520
521 #[cfg(feature = "bluetooth-test")]
522 pub fn set_service_data(
523 &self,
524 service_data: HashMap<String, Vec<u8>>,
525 ) -> Result<(), Box<dyn Error>> {
526 get_inner_and_call_test_func!(self, BluetoothDevice, set_service_data, Some(service_data))
527 }
528}
529
530#[derive(Clone, Debug)]
531pub enum BluetoothGATTService {
532 #[cfg(feature = "native-bluetooth")]
533 Btleplug(BtleplugGATTService),
534 #[cfg(feature = "bluetooth-test")]
535 Mock(Arc<FakeBluetoothGATTService>),
536}
537
538impl BluetoothGATTService {
539 fn create_service(device: BluetoothDevice, service: String) -> BluetoothGATTService {
540 match device {
541 #[cfg(feature = "native-bluetooth")]
542 BluetoothDevice::Btleplug(_) => {
543 unreachable!("btleplug services are created directly, not via create_service")
544 },
545 #[cfg(feature = "bluetooth-test")]
546 BluetoothDevice::Mock(fake_device) => BluetoothGATTService::Mock(
547 FakeBluetoothGATTService::new_empty(fake_device, service),
548 ),
549 }
550 }
551
552 #[cfg(feature = "bluetooth-test")]
553 pub fn create_mock_service(
554 device: BluetoothDevice,
555 service: String,
556 ) -> Result<BluetoothGATTService, Box<dyn Error>> {
557 match device {
558 BluetoothDevice::Mock(fake_device) => Ok(BluetoothGATTService::Mock(
559 FakeBluetoothGATTService::new_empty(fake_device, service),
560 )),
561 #[cfg(feature = "native-bluetooth")]
562 _ => Err(Box::from("The first parameter must be a mock structure")),
563 }
564 }
565
566 pub fn get_id(&self) -> String {
567 match self {
568 #[cfg(feature = "native-bluetooth")]
569 Self::Btleplug(inner) => inner.get_id(),
570 #[cfg(feature = "bluetooth-test")]
571 Self::Mock(inner) => inner.get_id(),
572 }
573 }
574
575 pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
576 match self {
577 #[cfg(feature = "native-bluetooth")]
578 Self::Btleplug(inner) => inner.get_uuid(),
579 #[cfg(feature = "bluetooth-test")]
580 Self::Mock(inner) => inner.get_uuid(),
581 }
582 }
583
584 pub fn is_primary(&self) -> Result<bool, Box<dyn Error>> {
585 match self {
586 #[cfg(feature = "native-bluetooth")]
587 Self::Btleplug(inner) => inner.is_primary(),
588 #[cfg(feature = "bluetooth-test")]
589 Self::Mock(inner) => inner.is_primary(),
590 }
591 }
592
593 pub fn get_includes(
594 &self,
595 device: BluetoothDevice,
596 ) -> Result<Vec<BluetoothGATTService>, Box<dyn Error>> {
597 let services = match self {
598 #[cfg(feature = "native-bluetooth")]
599 Self::Btleplug(inner) => inner.get_includes()?,
600 #[cfg(feature = "bluetooth-test")]
601 Self::Mock(inner) => inner.get_includes()?,
602 };
603 Ok(services
604 .into_iter()
605 .map(|service| BluetoothGATTService::create_service(device.clone(), service))
606 .collect())
607 }
608
609 pub fn get_gatt_characteristics(
610 &self,
611 ) -> Result<Vec<BluetoothGATTCharacteristic>, Box<dyn Error>> {
612 match self {
613 #[cfg(feature = "native-bluetooth")]
614 Self::Btleplug(inner) => Ok(inner.get_gatt_characteristics()),
615 #[cfg(feature = "bluetooth-test")]
616 Self::Mock(inner) => {
617 let characteristics = inner.get_gatt_characteristics()?;
618 Ok(characteristics
619 .into_iter()
620 .map(|characteristic| {
621 BluetoothGATTCharacteristic::Mock(
622 FakeBluetoothGATTCharacteristic::new_empty(
623 inner.clone(),
624 characteristic,
625 ),
626 )
627 })
628 .collect())
629 },
630 }
631 }
632
633 #[cfg(feature = "bluetooth-test")]
634 pub fn set_id(&self, id: String) {
635 #[allow(irrefutable_let_patterns)]
636 let Self::Mock(inner) = self else {
637 return;
638 };
639 inner.set_id(id);
640 }
641
642 #[cfg(feature = "bluetooth-test")]
643 pub fn set_uuid(&self, uuid: String) -> Result<(), Box<dyn Error>> {
644 get_inner_and_call_test_func!(self, BluetoothGATTService, set_uuid, uuid)
645 }
646
647 #[cfg(feature = "bluetooth-test")]
648 pub fn set_primary(&self, primary: bool) -> Result<(), Box<dyn Error>> {
649 get_inner_and_call_test_func!(self, BluetoothGATTService, set_is_primary, primary)
650 }
651}
652
653#[derive(Clone, Debug)]
654pub enum BluetoothGATTCharacteristic {
655 #[cfg(feature = "native-bluetooth")]
656 Btleplug(BtleplugGATTCharacteristic),
657 #[cfg(feature = "bluetooth-test")]
658 Mock(Arc<FakeBluetoothGATTCharacteristic>),
659}
660
661impl BluetoothGATTCharacteristic {
662 #[cfg(feature = "bluetooth-test")]
663 pub fn create_mock_characteristic(
664 service: BluetoothGATTService,
665 characteristic: String,
666 ) -> Result<BluetoothGATTCharacteristic, Box<dyn Error>> {
667 match service {
668 BluetoothGATTService::Mock(fake_service) => Ok(BluetoothGATTCharacteristic::Mock(
669 FakeBluetoothGATTCharacteristic::new_empty(fake_service, characteristic),
670 )),
671 #[cfg(feature = "native-bluetooth")]
672 _ => Err(Box::from("The first parameter must be a mock structure")),
673 }
674 }
675
676 pub fn get_id(&self) -> String {
677 match self {
678 #[cfg(feature = "native-bluetooth")]
679 Self::Btleplug(inner) => inner.get_id(),
680 #[cfg(feature = "bluetooth-test")]
681 Self::Mock(inner) => inner.get_id(),
682 }
683 }
684
685 pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
686 match self {
687 #[cfg(feature = "native-bluetooth")]
688 Self::Btleplug(inner) => inner.get_uuid(),
689 #[cfg(feature = "bluetooth-test")]
690 Self::Mock(inner) => inner.get_uuid(),
691 }
692 }
693
694 pub fn get_flags(&self) -> Result<Vec<String>, Box<dyn Error>> {
695 match self {
696 #[cfg(feature = "native-bluetooth")]
697 Self::Btleplug(inner) => inner.get_flags(),
698 #[cfg(feature = "bluetooth-test")]
699 Self::Mock(inner) => inner.get_flags(),
700 }
701 }
702
703 pub fn get_gatt_descriptors(&self) -> Result<Vec<BluetoothGATTDescriptor>, Box<dyn Error>> {
704 match self {
705 #[cfg(feature = "native-bluetooth")]
706 Self::Btleplug(inner) => Ok(inner.get_gatt_descriptors()),
707 #[cfg(feature = "bluetooth-test")]
708 Self::Mock(inner) => {
709 let descriptors = inner.get_gatt_descriptors()?;
710 Ok(descriptors
711 .into_iter()
712 .map(|descriptor| {
713 BluetoothGATTDescriptor::Mock(FakeBluetoothGATTDescriptor::new_empty(
714 inner.clone(),
715 descriptor,
716 ))
717 })
718 .collect())
719 },
720 }
721 }
722
723 pub async fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
724 match self {
725 #[cfg(feature = "native-bluetooth")]
726 Self::Btleplug(inner) => inner.read_value().await,
727 #[cfg(feature = "bluetooth-test")]
728 Self::Mock(inner) => inner.read_value(),
729 }
730 }
731
732 pub async fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
733 match self {
734 #[cfg(feature = "native-bluetooth")]
735 Self::Btleplug(inner) => inner.write_value(values).await,
736 #[cfg(feature = "bluetooth-test")]
737 Self::Mock(inner) => inner.write_value(values),
738 }
739 }
740
741 pub async fn start_notify(&self) -> Result<(), Box<dyn Error>> {
742 match self {
743 #[cfg(feature = "native-bluetooth")]
744 Self::Btleplug(inner) => inner.start_notify().await,
745 #[cfg(feature = "bluetooth-test")]
746 Self::Mock(inner) => inner.start_notify(),
747 }
748 }
749
750 pub async fn stop_notify(&self) -> Result<(), Box<dyn Error>> {
751 match self {
752 #[cfg(feature = "native-bluetooth")]
753 Self::Btleplug(inner) => inner.stop_notify().await,
754 #[cfg(feature = "bluetooth-test")]
755 Self::Mock(inner) => inner.stop_notify(),
756 }
757 }
758
759 #[cfg(feature = "bluetooth-test")]
760 pub fn set_id(&self, id: String) {
761 #[allow(irrefutable_let_patterns)]
762 let Self::Mock(inner) = self else {
763 return;
764 };
765 inner.set_id(id);
766 }
767
768 #[cfg(feature = "bluetooth-test")]
769 pub fn set_uuid(&self, uuid: String) -> Result<(), Box<dyn Error>> {
770 get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_uuid, uuid)
771 }
772
773 #[cfg(feature = "bluetooth-test")]
774 pub fn set_value(&self, value: Vec<u8>) -> Result<(), Box<dyn Error>> {
775 get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_value, Some(value))
776 }
777
778 #[cfg(feature = "bluetooth-test")]
779 pub fn set_flags(&self, flags: Vec<String>) -> Result<(), Box<dyn Error>> {
780 get_inner_and_call_test_func!(self, BluetoothGATTCharacteristic, set_flags, flags)
781 }
782}
783
784#[derive(Clone, Debug)]
785pub enum BluetoothGATTDescriptor {
786 #[cfg(feature = "native-bluetooth")]
787 Btleplug(BtleplugGATTDescriptor),
788 #[cfg(feature = "bluetooth-test")]
789 Mock(Arc<FakeBluetoothGATTDescriptor>),
790}
791
792impl BluetoothGATTDescriptor {
793 #[cfg(feature = "bluetooth-test")]
794 pub fn create_mock_descriptor(
795 characteristic: BluetoothGATTCharacteristic,
796 descriptor: String,
797 ) -> Result<BluetoothGATTDescriptor, Box<dyn Error>> {
798 #[allow(unreachable_patterns)]
799 match characteristic {
800 BluetoothGATTCharacteristic::Mock(fake_characteristic) => {
801 Ok(BluetoothGATTDescriptor::Mock(
802 FakeBluetoothGATTDescriptor::new_empty(fake_characteristic, descriptor),
803 ))
804 },
805 _ => Err(Box::from("The first parameter must be a mock structure")),
806 }
807 }
808
809 pub fn get_id(&self) -> String {
810 match self {
811 #[cfg(feature = "native-bluetooth")]
812 Self::Btleplug(inner) => inner.get_id(),
813 #[cfg(feature = "bluetooth-test")]
814 Self::Mock(inner) => inner.get_id(),
815 }
816 }
817
818 pub fn get_uuid(&self) -> Result<String, Box<dyn Error>> {
819 match self {
820 #[cfg(feature = "native-bluetooth")]
821 Self::Btleplug(inner) => inner.get_uuid(),
822 #[cfg(feature = "bluetooth-test")]
823 Self::Mock(inner) => inner.get_uuid(),
824 }
825 }
826
827 pub async fn read_value(&self) -> Result<Vec<u8>, Box<dyn Error>> {
828 match self {
829 #[cfg(feature = "native-bluetooth")]
830 Self::Btleplug(inner) => inner.read_value().await,
831 #[cfg(feature = "bluetooth-test")]
832 Self::Mock(inner) => inner.read_value(),
833 }
834 }
835
836 pub async fn write_value(&self, values: Vec<u8>) -> Result<(), Box<dyn Error>> {
837 match self {
838 #[cfg(feature = "native-bluetooth")]
839 Self::Btleplug(inner) => inner.write_value(values).await,
840 #[cfg(feature = "bluetooth-test")]
841 Self::Mock(inner) => inner.write_value(values),
842 }
843 }
844
845 #[cfg(feature = "bluetooth-test")]
846 pub fn set_id(&self, id: String) {
847 #[allow(irrefutable_let_patterns)]
848 let Self::Mock(inner) = self else {
849 return;
850 };
851 inner.set_id(id);
852 }
853
854 #[cfg(feature = "bluetooth-test")]
855 pub fn set_uuid(&self, uuid: String) -> Result<(), Box<dyn Error>> {
856 get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_uuid, uuid)
857 }
858
859 #[cfg(feature = "bluetooth-test")]
860 pub fn set_value(&self, value: Vec<u8>) -> Result<(), Box<dyn Error>> {
861 get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_value, Some(value))
862 }
863
864 #[cfg(feature = "bluetooth-test")]
865 pub fn set_flags(&self, flags: Vec<String>) -> Result<(), Box<dyn Error>> {
866 get_inner_and_call_test_func!(self, BluetoothGATTDescriptor, set_flags, flags)
867 }
868}