#![doc = include_str!("../README.md")]
use std::sync::mpsc::Receiver;
use async_trait::async_trait;
mod bluetooth;
pub mod devices;
mod ftms;
use devices::{DebugBike, Iconsole0028Bike, NonBluetoothDevice};
#[allow(dead_code)]
pub enum EquipmentType {
Iconsole0028Bike,
DebugBike,
NonBluetoothDevice,
}
#[async_trait]
pub trait Equipment {
async fn new(max_level: i16, shutdown_rx: &mut Receiver<()>) -> anyhow::Result<Self>
where
Self: Sized;
async fn connect(&mut self) -> anyhow::Result<bool>;
async fn disconnect(&self) -> anyhow::Result<()>;
async fn set_target_cadence(&self, rpm: i16) -> anyhow::Result<()>;
async fn set_target_power(&self, watts: i16) -> anyhow::Result<()>;
async fn read(&self) -> anyhow::Result<Option<ftms::FTMSData>>;
}
pub async fn equipment_type_to_equipment(
equipment_type: EquipmentType,
max_level: i16,
shutdown_rx: &mut Receiver<()>,
) -> Option<Box<dyn Equipment>> {
match equipment_type {
EquipmentType::Iconsole0028Bike => {
let equip = Iconsole0028Bike::new(max_level, shutdown_rx).await;
if equip.is_err() {
return None;
}
Some(Box::new(equip.unwrap()))
}
EquipmentType::DebugBike => {
let equip = DebugBike::new(max_level, shutdown_rx).await;
if equip.is_err() {
return None;
}
Some(Box::new(equip.unwrap()))
}
EquipmentType::NonBluetoothDevice => {
let equip = NonBluetoothDevice::new(max_level, shutdown_rx).await;
if equip.is_err() {
return None;
}
Some(Box::new(equip.unwrap()))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_equipment_type_to_equipment() -> anyhow::Result<()> {
let (_, mut shutdown_rx) = std::sync::mpsc::channel();
let equipment =
equipment_type_to_equipment(EquipmentType::NonBluetoothDevice, 10, &mut shutdown_rx)
.await;
assert!(equipment.is_some());
let mut equipment = equipment.unwrap();
assert!(equipment.connect().await?);
assert!(equipment.read().await?.is_some());
Ok(())
}
#[tokio::test]
async fn test_shutdown_while_scanning() -> anyhow::Result<()> {
let (shutdown_tx, mut shutdown_rx) = std::sync::mpsc::channel();
let _ = shutdown_tx.send(());
let equipment = Iconsole0028Bike::new(10, &mut shutdown_rx).await;
assert!(equipment.is_err());
Ok(())
}
#[tokio::test]
async fn test_shutdown_while_scanning_from_equipment_type() -> anyhow::Result<()> {
let (shutdown_tx, mut shutdown_rx) = std::sync::mpsc::channel();
let _ = shutdown_tx.send(());
let equipment =
equipment_type_to_equipment(EquipmentType::Iconsole0028Bike, 10, &mut shutdown_rx)
.await;
assert!(equipment.is_none());
Ok(())
}
}