servo_bluetooth/
adapter.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::error::Error;
6use std::sync::Arc;
7
8#[cfg(feature = "bluetooth-test")]
9use blurmock::fake_adapter::FakeBluetoothAdapter;
10#[cfg(feature = "bluetooth-test")]
11use blurmock::fake_device::FakeBluetoothDevice;
12#[cfg(feature = "bluetooth-test")]
13use blurmock::fake_discovery_session::FakeBluetoothDiscoverySession;
14#[cfg(feature = "native-bluetooth")]
15use btleplug::api::{Central, CentralState, Manager};
16#[cfg(feature = "native-bluetooth")]
17use btleplug::platform::{Adapter, Manager as PlatformManager};
18
19use super::bluetooth::{BluetoothDevice, BluetoothDiscoverySession};
20use crate::macros::get_inner_and_call_test_func;
21
22#[cfg(feature = "native-bluetooth")]
23#[derive(Clone, Debug)]
24pub struct BtleplugAdapter {
25    adapter: Adapter,
26}
27
28#[cfg(feature = "native-bluetooth")]
29impl BtleplugAdapter {
30    pub async fn new() -> Result<Self, Box<dyn Error>> {
31        let manager = PlatformManager::new().await?;
32        let adapters = manager.adapters().await?;
33        let adapter = adapters
34            .into_iter()
35            .next()
36            .ok_or_else(|| btleplug::Error::NoAdapterAvailable)?;
37        Ok(BtleplugAdapter { adapter })
38    }
39
40    pub async fn get_address(&self) -> Result<String, Box<dyn Error>> {
41        Ok(self.adapter.adapter_info().await?)
42    }
43
44    pub async fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
45        let state = self.adapter.adapter_state().await?;
46        Ok(state == CentralState::PoweredOn)
47    }
48
49    pub async fn get_devices(&self) -> Result<Vec<BluetoothDevice>, Box<dyn Error>> {
50        let peripherals = self.adapter.peripherals().await?;
51        Ok(peripherals
52            .into_iter()
53            .map(|p| BluetoothDevice::Btleplug(super::bluetooth::BtleplugDevice { peripheral: p }))
54            .collect())
55    }
56
57    pub fn create_discovery_session(&self) -> Result<BluetoothDiscoverySession, Box<dyn Error>> {
58        Ok(BluetoothDiscoverySession::Btleplug(
59            super::bluetooth::BtleplugDiscoverySession {
60                adapter: self.adapter.clone(),
61            },
62        ))
63    }
64}
65
66#[derive(Clone, Debug)]
67pub enum BluetoothAdapter {
68    #[cfg(feature = "native-bluetooth")]
69    Btleplug(BtleplugAdapter),
70    #[cfg(feature = "bluetooth-test")]
71    Mock(Arc<FakeBluetoothAdapter>),
72}
73
74impl BluetoothAdapter {
75    #[cfg(feature = "native-bluetooth")]
76    pub async fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
77        Ok(Self::Btleplug(BtleplugAdapter::new().await?))
78    }
79
80    #[cfg(not(feature = "native-bluetooth"))]
81    pub fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
82        Err(Box::from("Bluetooth not supported on this platform"))
83    }
84
85    #[cfg(feature = "bluetooth-test")]
86    pub fn new_mock() -> Result<BluetoothAdapter, Box<dyn Error>> {
87        Ok(Self::Mock(FakeBluetoothAdapter::new_empty()))
88    }
89
90    pub async fn get_address(&self) -> Result<String, Box<dyn Error>> {
91        match self {
92            #[cfg(feature = "native-bluetooth")]
93            Self::Btleplug(inner) => inner.get_address().await,
94            #[cfg(feature = "bluetooth-test")]
95            Self::Mock(inner) => inner.get_address(),
96        }
97    }
98
99    pub async fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
100        match self {
101            #[cfg(feature = "native-bluetooth")]
102            Self::Btleplug(inner) => inner.is_powered().await,
103            #[cfg(feature = "bluetooth-test")]
104            Self::Mock(inner) => inner.is_powered(),
105        }
106    }
107
108    pub async fn get_devices(&self) -> Result<Vec<BluetoothDevice>, Box<dyn Error>> {
109        match self {
110            #[cfg(feature = "native-bluetooth")]
111            Self::Btleplug(inner) => inner.get_devices().await,
112            #[cfg(feature = "bluetooth-test")]
113            Self::Mock(inner) => {
114                let device_list = inner.get_device_list()?;
115                Ok(device_list
116                    .into_iter()
117                    .map(|device| {
118                        BluetoothDevice::Mock(FakeBluetoothDevice::new_empty(inner.clone(), device))
119                    })
120                    .collect())
121            },
122        }
123    }
124
125    pub fn create_discovery_session(&self) -> Result<BluetoothDiscoverySession, Box<dyn Error>> {
126        match self {
127            #[cfg(feature = "native-bluetooth")]
128            Self::Btleplug(inner) => inner.create_discovery_session(),
129            #[cfg(feature = "bluetooth-test")]
130            Self::Mock(inner) => Ok(BluetoothDiscoverySession::Mock(Arc::new(
131                FakeBluetoothDiscoverySession::create_session(inner.clone())?,
132            ))),
133        }
134    }
135
136    pub fn create_mock_device(&self, _device: String) -> Result<BluetoothDevice, Box<dyn Error>> {
137        match self {
138            #[cfg(feature = "bluetooth-test")]
139            Self::Mock(inner) => Ok(BluetoothDevice::Mock(FakeBluetoothDevice::new_empty(
140                inner.clone(),
141                _device,
142            ))),
143            #[cfg(feature = "native-bluetooth")]
144            _ => Err(Box::from("Test functions not supported on real devices")),
145        }
146    }
147
148    #[cfg(feature = "bluetooth-test")]
149    pub fn set_name(&self, name: String) -> Result<(), Box<dyn Error>> {
150        get_inner_and_call_test_func!(self, BluetoothAdapter, set_name, name)
151    }
152
153    #[cfg(feature = "bluetooth-test")]
154    pub fn set_powered(&self, powered: bool) -> Result<(), Box<dyn Error>> {
155        get_inner_and_call_test_func!(self, BluetoothAdapter, set_powered, powered)
156    }
157
158    #[cfg(feature = "bluetooth-test")]
159    pub fn is_present(&self) -> Result<bool, Box<dyn Error>> {
160        get_inner_and_call_test_func!(self, BluetoothAdapter, is_present)
161    }
162
163    #[cfg(feature = "bluetooth-test")]
164    pub fn set_present(&self, present: bool) -> Result<(), Box<dyn Error>> {
165        get_inner_and_call_test_func!(self, BluetoothAdapter, set_present, present)
166    }
167
168    #[cfg(feature = "bluetooth-test")]
169    pub fn set_discoverable(&self, discoverable: bool) -> Result<(), Box<dyn Error>> {
170        get_inner_and_call_test_func!(self, BluetoothAdapter, set_discoverable, discoverable)
171    }
172}