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