[fp-rs] Implemented custom error types for Bluetooth library.

This commit is contained in:
Lucas Silva Shepard
2023-07-29 17:18:39 -07:00
parent 251740d010
commit 494eb66027
13 changed files with 110 additions and 34 deletions
+1 -1
View File
@@ -20,11 +20,11 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0"
futures = { version = "0.3", features = ["executor"] }
tracing = "0.1.37"
cfg-if = "1.0.0"
async-trait = "0.1"
thiserror = "1.0.43"
[target.'cfg(windows)'.dependencies]
windows = { version = "0.48", features = [
@@ -14,7 +14,7 @@
use async_trait::async_trait;
use super::Device;
use super::{BluetoothError, Device};
/// Concrete types implementing this trait are Bluetooth Central devices.
/// They provide methods for retrieving nearby connections and device info.
@@ -23,14 +23,14 @@ pub trait Adapter: Sized {
type Device: Device;
/// Retrieve the system-default Bluetooth adapter.
async fn default() -> Result<Self, anyhow::Error>;
async fn default() -> Result<Self, BluetoothError>;
/// Begin scanning for nearby devices.
fn start_scan_devices(&mut self) -> Result<(), anyhow::Error>;
fn start_scan_devices(&mut self) -> Result<(), BluetoothError>;
/// Stop scanning for nearby devices.
fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error>;
fn stop_scan_devices(&mut self) -> Result<(), BluetoothError>;
/// Poll next discovered device.
async fn next_device(&mut self) -> Result<Self::Device, anyhow::Error>;
async fn next_device(&mut self) -> Result<Self::Device, BluetoothError>;
}
+3 -1
View File
@@ -15,7 +15,9 @@
/// Concrete types implementing this trait represent Bluetooth Peripheral devices.
/// They provide methods for retrieving device info and running device actions,
/// such as pairing.
use super::BluetoothError;
pub trait Device {
/// Retrieve the name advertised by this device.
fn name(&self) -> Result<String, anyhow::Error>;
fn name(&self) -> Result<String, BluetoothError>;
}
@@ -0,0 +1,42 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use thiserror::Error;
/// Library error type.
#[non_exhaustive]
#[derive(Error, Debug)]
pub enum BluetoothError {
/// Indicates that the operation was rejected because the system is not in
/// a state required for the operation's execution.
/// E.g. The user calls `stop_scan()` or polls the advertisement stream
/// before calling `start_scan()`.
#[error("failed precondition: {0}")]
FailedPrecondition(String),
/// Reported when the user calls an operation that is supported by their
/// Operating System, but is not supported by their device.
/// E.g. a Windows machine with an old BT Classic adapter that
/// doesn't support BLE).
#[error("bluetooth operation not supported by system: {0}")]
NotSupported(String),
/// Wrapper around OS-level errors, e.g. `windows::core::Error` for Windows.
/// These typically mean something is very wrong with the system (e.g. OOM).
#[error("bluetooth system-level error: {0}")]
System(String),
/// Reported when a bug occurs inside the library. Whenever a seemingly
/// impossible error condition arises where you could call `expect()`,
/// return this error instead.
#[error("internal error: {0}")]
Internal(String),
}
@@ -15,6 +15,8 @@
/// Module for shared functionality between all Bluetooth platforms.
mod adapter;
mod device;
mod error;
pub use adapter::*;
pub use device::*;
pub use error::*;
+2 -2
View File
@@ -18,7 +18,7 @@
pub mod common;
pub use common::{Adapter, Device};
pub use common::{Adapter, BluetoothError, Device};
cfg_if::cfg_if! {
if #[cfg(windows)] {
@@ -30,6 +30,6 @@ cfg_if::cfg_if! {
}
}
pub async fn default_adapter() -> Result<impl Adapter, anyhow::Error> {
pub async fn default_adapter() -> Result<impl Adapter, BluetoothError> {
BleAdapter::default().await
}
@@ -15,7 +15,7 @@
use async_trait::async_trait;
use super::BleDevice;
use crate::bluetooth::common::Adapter;
use crate::bluetooth::common::{Adapter, BluetoothError};
/// Concrete type implementing `Adapter`, used for unsupported devices.
/// Every method should panic.
@@ -25,19 +25,19 @@ pub struct BleAdapter;
impl Adapter for BleAdapter {
type Device = BleDevice;
async fn default() -> Result<Self, anyhow::Error> {
async fn default() -> Result<Self, BluetoothError> {
panic!("Unsupported target platform.");
}
fn start_scan_devices(&mut self) -> Result<(), anyhow::Error> {
fn start_scan_devices(&mut self) -> Result<(), BluetoothError> {
panic!("Unsupported target platform.");
}
fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error> {
fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> {
panic!("Unsupported target platform.");
}
async fn next_device(&mut self) -> Result<Self::Device, anyhow::Error> {
async fn next_device(&mut self) -> Result<Self::Device, BluetoothError> {
panic!("Unsupported target platform.");
}
}
@@ -12,14 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::bluetooth::common::Device;
use crate::bluetooth::common::{BluetoothError, Device};
/// Concrete type implementing `Device`, used for unsupported devices.
/// Every method should panic.
pub struct BleDevice;
impl Device for BleDevice {
fn name(&self) -> Result<String, anyhow::Error> {
fn name(&self) -> Result<String, BluetoothError> {
panic!("Unsupported target platform.")
}
}
+20 -14
View File
@@ -55,7 +55,7 @@ use windows::{
};
use super::BleDevice;
use crate::bluetooth::common::Adapter;
use crate::bluetooth::common::{Adapter, BluetoothError};
/// Concrete type implementing `Adapter`, used for Windows BLE.
pub struct BleAdapter {
@@ -71,18 +71,18 @@ pub struct BleAdapter {
impl Adapter for BleAdapter {
type Device = BleDevice;
async fn default() -> Result<Self, anyhow::Error> {
async fn default() -> Result<Self, BluetoothError> {
let inner = BluetoothAdapter::GetDefaultAsync()?.await?;
if !inner.IsLowEnergySupported()? {
return Err(anyhow::anyhow!(
"This device's Bluetooth Adapter doesn't support Bluetooth LE Transport type."
));
return Err(BluetoothError::NotSupported(String::from(
"LE transport type",
)));
}
if !inner.IsCentralRoleSupported()? {
return Err(anyhow::anyhow!(
"This device's Bluetooth Adapter doesn't support Bluetooth LE central role."
));
return Err(BluetoothError::NotSupported(String::from(
"central role",
)));
}
Ok(BleAdapter {
@@ -91,7 +91,7 @@ impl Adapter for BleAdapter {
})
}
fn start_scan_devices(&mut self) -> Result<(), anyhow::Error> {
fn start_scan_devices(&mut self) -> Result<(), BluetoothError> {
let watcher = BluetoothLEAdvertisementWatcher::new()?;
match watcher.SetScanningMode(BluetoothLEScanningMode::Active) {
Ok(_) => (),
@@ -194,23 +194,29 @@ impl Adapter for BleAdapter {
Ok(())
}
fn stop_scan_devices(&mut self) -> Result<(), anyhow::Error> {
fn stop_scan_devices(&mut self) -> Result<(), BluetoothError> {
if let Some(_) = &self.device_stream {
self.device_stream.take();
Ok(())
} else {
Err(anyhow::anyhow!("Device scanning hasn't started."))
Err(BluetoothError::FailedPrecondition(String::from(
"device scanning hasn't started, please call `start_scan()`",
)))
}
}
async fn next_device(&mut self) -> Result<Self::Device, anyhow::Error> {
async fn next_device(&mut self) -> Result<Self::Device, BluetoothError> {
if let Some(stream) = &mut self.device_stream {
stream
.next()
.await
.ok_or(anyhow::anyhow!("Device returned from stream is None."))
.ok_or(BluetoothError::Internal(String::from(
"device returned from Stream is None",
)))
} else {
Err(anyhow::anyhow!("Device scanning hasn't started."))
Err(BluetoothError::FailedPrecondition(String::from(
"device scanning hasn't started, please call `start_scan()`",
)))
}
}
}
@@ -23,7 +23,7 @@ use windows::Devices::Bluetooth::{
BluetoothLEDevice,
};
use crate::bluetooth::common::Device;
use crate::bluetooth::common::{BluetoothError, Device};
/// Concrete type implementing `Device`, used for Windows BLE.
pub struct BleDevice {
@@ -35,7 +35,7 @@ impl BleDevice {
pub(super) async fn from_addr(
addr: u64,
kind: BluetoothAddressType,
) -> Result<Self, anyhow::Error> {
) -> Result<Self, BluetoothError> {
let inner =
BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync(addr, kind)?
.await?;
@@ -46,7 +46,7 @@ impl BleDevice {
#[async_trait]
impl Device for BleDevice {
fn name(&self) -> Result<String, anyhow::Error> {
fn name(&self) -> Result<String, BluetoothError> {
Ok(self.inner.Name()?.to_string_lossy())
}
}
@@ -0,0 +1,21 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::bluetooth::common::BluetoothError;
impl From<windows::core::Error> for BluetoothError {
fn from(err: windows::core::Error) -> Self {
BluetoothError::System(err.to_string())
}
}
@@ -15,6 +15,7 @@
/// Bluetooth LE module for Windows devices.
mod adapter;
mod device;
mod error;
pub use adapter::*;
pub use device::*;
+3 -1
View File
@@ -12,13 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::error::Error;
use futures::executor;
mod bluetooth;
use bluetooth::{Adapter, Device};
fn main() -> Result<(), anyhow::Error> {
fn main() -> Result<(), Box<dyn Error>> {
let run = async {
let mut adapter = bluetooth::default_adapter().await?;
adapter.start_scan_devices()?;