Skip to main content

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};
20
21#[cfg(feature = "native-bluetooth")]
22#[derive(Clone, Debug)]
23pub struct BtleplugAdapter {
24    adapter: Adapter,
25}
26
27#[cfg(feature = "native-bluetooth")]
28impl BtleplugAdapter {
29    pub async fn new() -> Result<Self, Box<dyn Error>> {
30        let manager = PlatformManager::new().await?;
31        let adapters = manager.adapters().await?;
32        let adapter = adapters
33            .into_iter()
34            .next()
35            .ok_or_else(|| btleplug::Error::NoAdapterAvailable)?;
36        Ok(BtleplugAdapter { adapter })
37    }
38
39    pub async fn get_address(&self) -> Result<String, Box<dyn Error>> {
40        Ok(self.adapter.adapter_info().await?)
41    }
42
43    pub async fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
44        let state = self.adapter.adapter_state().await?;
45        Ok(state == CentralState::PoweredOn)
46    }
47
48    pub async fn get_devices(&self) -> Result<Vec<BluetoothDevice>, Box<dyn Error>> {
49        let peripherals = self.adapter.peripherals().await?;
50        Ok(peripherals
51            .into_iter()
52            .map(|p| BluetoothDevice::Btleplug(super::bluetooth::BtleplugDevice { peripheral: p }))
53            .collect())
54    }
55
56    pub fn create_discovery_session(&self) -> Result<BluetoothDiscoverySession, Box<dyn Error>> {
57        Ok(BluetoothDiscoverySession::Btleplug(
58            super::bluetooth::BtleplugDiscoverySession {
59                adapter: self.adapter.clone(),
60            },
61        ))
62    }
63}
64
65#[derive(Clone, Debug)]
66pub enum BluetoothAdapter {
67    #[cfg(feature = "native-bluetooth")]
68    Btleplug(BtleplugAdapter),
69    #[cfg(feature = "bluetooth-test")]
70    Mock(Arc<FakeBluetoothAdapter>),
71}
72
73impl BluetoothAdapter {
74    #[cfg(feature = "native-bluetooth")]
75    pub async fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
76        Ok(Self::Btleplug(BtleplugAdapter::new().await?))
77    }
78
79    #[cfg(not(feature = "native-bluetooth"))]
80    pub fn new() -> Result<BluetoothAdapter, Box<dyn Error>> {
81        Err(Box::from("Bluetooth not supported on this platform"))
82    }
83
84    #[cfg(feature = "bluetooth-test")]
85    pub fn new_mock() -> Result<BluetoothAdapter, Box<dyn Error>> {
86        Ok(Self::Mock(FakeBluetoothAdapter::new_empty()))
87    }
88
89    pub async fn get_address(&self) -> Result<String, Box<dyn Error>> {
90        match self {
91            #[cfg(feature = "native-bluetooth")]
92            Self::Btleplug(inner) => inner.get_address().await,
93            #[cfg(feature = "bluetooth-test")]
94            Self::Mock(inner) => inner.get_address(),
95        }
96    }
97
98    pub async fn is_powered(&self) -> Result<bool, Box<dyn Error>> {
99        match self {
100            #[cfg(feature = "native-bluetooth")]
101            Self::Btleplug(inner) => inner.is_powered().await,
102            #[cfg(feature = "bluetooth-test")]
103            Self::Mock(inner) => inner.is_powered(),
104        }
105    }
106
107    pub async fn get_devices(&self) -> Result<Vec<BluetoothDevice>, Box<dyn Error>> {
108        match self {
109            #[cfg(feature = "native-bluetooth")]
110            Self::Btleplug(inner) => inner.get_devices().await,
111            #[cfg(feature = "bluetooth-test")]
112            Self::Mock(inner) => {
113                let device_list = inner.get_device_list()?;
114                Ok(device_list
115                    .into_iter()
116                    .map(|device| {
117                        BluetoothDevice::Mock(FakeBluetoothDevice::new_empty(inner.clone(), device))
118                    })
119                    .collect())
120            },
121        }
122    }
123
124    pub fn create_discovery_session(&self) -> Result<BluetoothDiscoverySession, Box<dyn Error>> {
125        match self {
126            #[cfg(feature = "native-bluetooth")]
127            Self::Btleplug(inner) => inner.create_discovery_session(),
128            #[cfg(feature = "bluetooth-test")]
129            Self::Mock(inner) => Ok(BluetoothDiscoverySession::Mock(Arc::new(
130                FakeBluetoothDiscoverySession::create_session(inner.clone())?,
131            ))),
132        }
133    }
134
135    pub fn create_mock_device(&self, _device: String) -> Result<BluetoothDevice, Box<dyn Error>> {
136        match self {
137            #[cfg(feature = "bluetooth-test")]
138            Self::Mock(inner) => Ok(BluetoothDevice::Mock(FakeBluetoothDevice::new_empty(
139                inner.clone(),
140                _device,
141            ))),
142            #[cfg(feature = "native-bluetooth")]
143            _ => Err(Box::from("Test functions not supported on real devices")),
144        }
145    }
146
147    #[cfg(feature = "bluetooth-test")]
148    fn mock(&self) -> Result<&FakeBluetoothAdapter, Box<dyn Error>> {
149        match self {
150            Self::Mock(adapter) => Ok(adapter),
151            #[cfg(feature = "native-bluetooth")]
152            _ => Err(Box::from(
153                "Error! Test functions are not supported on real devices!",
154            )),
155        }
156    }
157
158    #[cfg(feature = "bluetooth-test")]
159    pub fn set_name(&self, name: String) -> Result<(), Box<dyn Error>> {
160        self.mock()?.set_name(name)
161    }
162
163    #[cfg(feature = "bluetooth-test")]
164    pub fn set_powered(&self, powered: bool) -> Result<(), Box<dyn Error>> {
165        self.mock()?.set_powered(powered)
166    }
167
168    #[cfg(feature = "bluetooth-test")]
169    pub fn is_present(&self) -> Result<bool, Box<dyn Error>> {
170        self.mock()?.is_present()
171    }
172
173    #[cfg(feature = "bluetooth-test")]
174    pub fn set_present(&self, present: bool) -> Result<(), Box<dyn Error>> {
175        self.mock()?.set_present(present)
176    }
177
178    #[cfg(feature = "bluetooth-test")]
179    pub fn set_discoverable(&self, discoverable: bool) -> Result<(), Box<dyn Error>> {
180        self.mock()?.set_discoverable(discoverable)
181    }
182}