From 494eb660271f050b31f17e67940b341e7d3e5290 Mon Sep 17 00:00:00 2001 From: Lucas Silva Shepard Date: Tue, 18 Jul 2023 15:18:53 -0700 Subject: [PATCH] [fp-rs] Implemented custom error types for Bluetooth library. --- fastpair/rust/Cargo.toml | 2 +- fastpair/rust/src/bluetooth/common/adapter.rs | 10 ++--- fastpair/rust/src/bluetooth/common/device.rs | 4 +- fastpair/rust/src/bluetooth/common/error.rs | 42 +++++++++++++++++++ fastpair/rust/src/bluetooth/common/mod.rs | 2 + fastpair/rust/src/bluetooth/mod.rs | 4 +- .../rust/src/bluetooth/unsupported/adapter.rs | 10 ++--- .../rust/src/bluetooth/unsupported/device.rs | 4 +- .../rust/src/bluetooth/windows/adapter.rs | 34 ++++++++------- fastpair/rust/src/bluetooth/windows/device.rs | 6 +-- fastpair/rust/src/bluetooth/windows/error.rs | 21 ++++++++++ fastpair/rust/src/bluetooth/windows/mod.rs | 1 + fastpair/rust/src/main.rs | 4 +- 13 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 fastpair/rust/src/bluetooth/common/error.rs create mode 100644 fastpair/rust/src/bluetooth/windows/error.rs diff --git a/fastpair/rust/Cargo.toml b/fastpair/rust/Cargo.toml index 20a09107..02a7b908 100644 --- a/fastpair/rust/Cargo.toml +++ b/fastpair/rust/Cargo.toml @@ -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 = [ diff --git a/fastpair/rust/src/bluetooth/common/adapter.rs b/fastpair/rust/src/bluetooth/common/adapter.rs index 5e8be0ae..40a504ee 100644 --- a/fastpair/rust/src/bluetooth/common/adapter.rs +++ b/fastpair/rust/src/bluetooth/common/adapter.rs @@ -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; + async fn default() -> Result; /// 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; + async fn next_device(&mut self) -> Result; } diff --git a/fastpair/rust/src/bluetooth/common/device.rs b/fastpair/rust/src/bluetooth/common/device.rs index 1849fcef..33ef11c2 100644 --- a/fastpair/rust/src/bluetooth/common/device.rs +++ b/fastpair/rust/src/bluetooth/common/device.rs @@ -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; + fn name(&self) -> Result; } diff --git a/fastpair/rust/src/bluetooth/common/error.rs b/fastpair/rust/src/bluetooth/common/error.rs new file mode 100644 index 00000000..3e7ac640 --- /dev/null +++ b/fastpair/rust/src/bluetooth/common/error.rs @@ -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), +} diff --git a/fastpair/rust/src/bluetooth/common/mod.rs b/fastpair/rust/src/bluetooth/common/mod.rs index ea2fa401..d0fce7c4 100644 --- a/fastpair/rust/src/bluetooth/common/mod.rs +++ b/fastpair/rust/src/bluetooth/common/mod.rs @@ -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::*; diff --git a/fastpair/rust/src/bluetooth/mod.rs b/fastpair/rust/src/bluetooth/mod.rs index b49b95df..765086f2 100644 --- a/fastpair/rust/src/bluetooth/mod.rs +++ b/fastpair/rust/src/bluetooth/mod.rs @@ -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 { +pub async fn default_adapter() -> Result { BleAdapter::default().await } diff --git a/fastpair/rust/src/bluetooth/unsupported/adapter.rs b/fastpair/rust/src/bluetooth/unsupported/adapter.rs index f7395ea5..8baa3b81 100644 --- a/fastpair/rust/src/bluetooth/unsupported/adapter.rs +++ b/fastpair/rust/src/bluetooth/unsupported/adapter.rs @@ -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 { + async fn default() -> Result { 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 { + async fn next_device(&mut self) -> Result { panic!("Unsupported target platform."); } } diff --git a/fastpair/rust/src/bluetooth/unsupported/device.rs b/fastpair/rust/src/bluetooth/unsupported/device.rs index 6c8bc520..d18d1e34 100644 --- a/fastpair/rust/src/bluetooth/unsupported/device.rs +++ b/fastpair/rust/src/bluetooth/unsupported/device.rs @@ -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 { + fn name(&self) -> Result { panic!("Unsupported target platform.") } } diff --git a/fastpair/rust/src/bluetooth/windows/adapter.rs b/fastpair/rust/src/bluetooth/windows/adapter.rs index 6d7af121..0cabf752 100644 --- a/fastpair/rust/src/bluetooth/windows/adapter.rs +++ b/fastpair/rust/src/bluetooth/windows/adapter.rs @@ -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 { + async fn default() -> Result { 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 { + async fn next_device(&mut self) -> Result { 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()`", + ))) } } } diff --git a/fastpair/rust/src/bluetooth/windows/device.rs b/fastpair/rust/src/bluetooth/windows/device.rs index 08c3d123..60bd872b 100644 --- a/fastpair/rust/src/bluetooth/windows/device.rs +++ b/fastpair/rust/src/bluetooth/windows/device.rs @@ -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 { + ) -> Result { let inner = BluetoothLEDevice::FromBluetoothAddressWithBluetoothAddressTypeAsync(addr, kind)? .await?; @@ -46,7 +46,7 @@ impl BleDevice { #[async_trait] impl Device for BleDevice { - fn name(&self) -> Result { + fn name(&self) -> Result { Ok(self.inner.Name()?.to_string_lossy()) } } diff --git a/fastpair/rust/src/bluetooth/windows/error.rs b/fastpair/rust/src/bluetooth/windows/error.rs new file mode 100644 index 00000000..04c6bce3 --- /dev/null +++ b/fastpair/rust/src/bluetooth/windows/error.rs @@ -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 for BluetoothError { + fn from(err: windows::core::Error) -> Self { + BluetoothError::System(err.to_string()) + } +} diff --git a/fastpair/rust/src/bluetooth/windows/mod.rs b/fastpair/rust/src/bluetooth/windows/mod.rs index 7d54d4ef..4835654d 100644 --- a/fastpair/rust/src/bluetooth/windows/mod.rs +++ b/fastpair/rust/src/bluetooth/windows/mod.rs @@ -15,6 +15,7 @@ /// Bluetooth LE module for Windows devices. mod adapter; mod device; +mod error; pub use adapter::*; pub use device::*; diff --git a/fastpair/rust/src/main.rs b/fastpair/rust/src/main.rs index 5905e135..c0326ba6 100644 --- a/fastpair/rust/src/main.rs +++ b/fastpair/rust/src/main.rs @@ -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> { let run = async { let mut adapter = bluetooth::default_adapter().await?; adapter.start_scan_devices()?;