diff --git a/google-home/src/device.rs b/google-home/src/device.rs index a9383ad..00dc87f 100644 --- a/google-home/src/device.rs +++ b/google-home/src/device.rs @@ -4,11 +4,11 @@ use crate::{ errors::{DeviceError, ErrorCode}, request::execute::CommandType, response, - traits::{AsOnOff, AsScene, Trait}, + traits::{As, OnOff, Scene, Trait}, types::Type, }; -pub trait GoogleHomeDevice: AsOnOff + AsScene { +pub trait GoogleHomeDevice: As + As + 'static { fn get_device_type(&self) -> Type; fn get_device_name(&self) -> Name; fn get_id(&self) -> &str; @@ -40,14 +40,14 @@ pub trait GoogleHomeDevice: AsOnOff + AsScene { let mut traits = Vec::new(); // OnOff - if let Some(on_off) = AsOnOff::cast(self) { + if let Some(on_off) = As::::cast(self) { traits.push(Trait::OnOff); device.attributes.command_only_on_off = on_off.is_command_only(); device.attributes.query_only_on_off = on_off.is_query_only(); } // Scene - if let Some(scene) = AsScene::cast(self) { + if let Some(scene) = As::::cast(self) { traits.push(Trait::Scene); device.attributes.scene_reversible = scene.is_scene_reversible(); } @@ -64,7 +64,7 @@ pub trait GoogleHomeDevice: AsOnOff + AsScene { } // OnOff - if let Some(on_off) = AsOnOff::cast(self) { + if let Some(on_off) = As::::cast(self) { device.state.on = on_off.is_on().map_err(|err| device.set_error(err)).ok(); } @@ -74,13 +74,13 @@ pub trait GoogleHomeDevice: AsOnOff + AsScene { fn execute(&mut self, command: &CommandType) -> Result<(), ErrorCode> { match command { CommandType::OnOff { on } => { - let on_off = AsOnOff::cast_mut(self) + let on_off = As::::cast_mut(self) .ok_or::(DeviceError::ActionNotAvailable.into())?; on_off.set_on(*on)?; } CommandType::ActivateScene { deactivate } => { - let scene = AsScene::cast_mut(self) + let scene = As::::cast_mut(self) .ok_or::(DeviceError::ActionNotAvailable.into())?; scene.set_active(!deactivate)?; diff --git a/google-home/src/traits.rs b/google-home/src/traits.rs index 7322dc7..2597d98 100644 --- a/google-home/src/traits.rs +++ b/google-home/src/traits.rs @@ -10,7 +10,11 @@ pub enum Trait { Scene, } -pub trait OnOff: std::fmt::Debug { +impl_cast::impl_setup!(); +impl_cast::impl_cast!(GoogleHomeDevice, OnOff); +impl_cast::impl_cast!(GoogleHomeDevice, Scene); + +pub trait OnOff: std::fmt::Debug + Sync + Send + 'static { fn is_command_only(&self) -> Option { None } @@ -23,13 +27,11 @@ pub trait OnOff: std::fmt::Debug { fn is_on(&self) -> Result; fn set_on(&mut self, on: bool) -> Result<(), ErrorCode>; } -impl_cast::impl_cast!(GoogleHomeDevice, OnOff); -pub trait Scene: std::fmt::Debug { +pub trait Scene: std::fmt::Debug + Sync + Send + 'static { fn is_scene_reversible(&self) -> Option { None } fn set_active(&self, activate: bool) -> Result<(), ErrorCode>; } -impl_cast::impl_cast!(GoogleHomeDevice, Scene); diff --git a/impl_cast/src/lib.rs b/impl_cast/src/lib.rs index d283aba..d6adba2 100644 --- a/impl_cast/src/lib.rs +++ b/impl_cast/src/lib.rs @@ -1,38 +1,47 @@ pub extern crate paste; +#[macro_export] +macro_rules! impl_setup { + () => { + pub trait As { + fn consume(self: Box) -> Option>; + fn cast(&self) -> Option<&T>; + fn cast_mut(&mut self) -> Option<&mut T>; + } + }; +} + #[macro_export] macro_rules! impl_cast { ($base:ident, $trait:ident) => { $crate::paste::paste! { - pub trait [< As $trait>] { - fn consume(self: Box) -> Option>; - fn cast(&self) -> Option<&dyn $trait>; - fn cast_mut(&mut self) -> Option<&mut dyn $trait>; - } - - impl [< As $trait>] for T { - default fn consume(self: Box) -> Option> { - None - } - default fn cast(&self) -> Option<&dyn $trait> { - None - } - default fn cast_mut(&mut self) -> Option<&mut dyn $trait> { - None - } - } - - impl [< As $trait>] for T { - fn consume(self: Box) -> Option> { + impl As for T { + fn consume(self: Box) -> Option> { Some(self) } + fn cast(&self) -> Option<&dyn $trait> { Some(self) } - fn cast_mut(&mut self) -> Option<&mut dyn $trait> { + + fn cast_mut(&mut self) -> Option<&mut dyn $trait> { Some(self) } } + + impl As for T { + default fn consume(self: Box) -> Option> { + None + } + + default fn cast(&self) -> Option<&dyn $trait> { + None + } + + default fn cast_mut(&mut self) -> Option<&mut dyn $trait> { + None + } + } } }; } diff --git a/src/config.rs b/src/config.rs index 701e339..248d460 100644 --- a/src/config.rs +++ b/src/config.rs @@ -12,7 +12,7 @@ use serde::Deserialize; use tracing::{debug, trace}; use crate::{ - devices::{self, AudioSetup, ContactSensor, DeviceBox, IkeaOutlet, KasaOutlet, WakeOnLAN}, + devices::{self, AudioSetup, ContactSensor, IkeaOutlet, KasaOutlet, WakeOnLAN}, error::{ConfigParseError, DeviceCreationError, MissingEnv, MissingWildcard}, }; @@ -255,10 +255,10 @@ impl Config { // Quick helper function to box up the devices, // passing in Box::new would be ideal, however the return type is incorrect // Maybe there is a better way to solve this? -fn device_box(device: T) -> DeviceBox { - let a: DeviceBox = Box::new(device); - a -} +// fn device_box(device: T) -> DeviceBox { +// let a: DeviceBox = Box::new(device); +// a +// } impl Device { #[async_recursion] @@ -267,8 +267,8 @@ impl Device { identifier: &str, config: &Config, client: AsyncClient, - ) -> Result { - let device = match self { + ) -> Result, DeviceCreationError> { + let device: Box = match self { Device::IkeaOutlet { info, mqtt, @@ -283,7 +283,7 @@ impl Device { ); IkeaOutlet::build(identifier, info, mqtt, outlet_type, timeout, client) .await - .map(device_box)? + .map(Box::new)? } Device::WakeOnLAN { info, @@ -299,11 +299,11 @@ impl Device { ); WakeOnLAN::build(identifier, info, mqtt, mac_address, broadcast_ip, client) .await - .map(device_box)? + .map(Box::new)? } Device::KasaOutlet { ip } => { trace!(id = identifier, "KasaOutlet [{}]", identifier); - device_box(KasaOutlet::new(identifier, ip)) + Box::new(KasaOutlet::new(identifier, ip)) } Device::AudioSetup { mqtt, @@ -321,7 +321,7 @@ impl Device { AudioSetup::build(identifier, mqtt, mixer, speakers, client) .await - .map(device_box)? + .map(Box::new)? } Device::ContactSensor { mqtt, presence } => { trace!(id = identifier, "ContactSensor [{}]", identifier); @@ -331,7 +331,7 @@ impl Device { ContactSensor::build(identifier, mqtt, presence, client) .await - .map(device_box)? + .map(Box::new)? } }; diff --git a/src/devices.rs b/src/devices.rs index 50abeb8..c9494ec 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -25,6 +25,7 @@ use crate::{ presence::{self, OnPresence}, }; +impl_cast::impl_setup!(); impl_cast::impl_cast!(Device, OnMqtt); impl_cast::impl_cast!(Device, OnPresence); impl_cast::impl_cast!(Device, OnDarkness); @@ -32,14 +33,15 @@ impl_cast::impl_cast!(Device, GoogleHomeDevice); impl_cast::impl_cast!(Device, OnOff); pub trait Device: - AsGoogleHomeDevice - + AsOnMqtt - + AsOnPresence - + AsOnDarkness - + AsOnOff + As + + As + + As + + As + + As + std::fmt::Debug + Sync + Send + + 'static { fn get_id(&self) -> &str; } @@ -47,7 +49,7 @@ pub trait Device: // TODO: Add an inner type that we can wrap with Arc> to make this type a little bit nicer // to work with struct Devices { - devices: HashMap, + devices: HashMap>, } macro_rules! get_cast { @@ -57,7 +59,7 @@ macro_rules! get_cast { self.devices .iter_mut() .filter_map(|(id, device)| { - [< As $trait >]::cast_mut(device.as_mut()) + As::::cast_mut(device.as_mut()) .map(|listener| (id.as_str(), listener)) }).collect() } @@ -73,13 +75,11 @@ pub enum Command { tx: oneshot::Sender>, }, AddDevice { - device: DeviceBox, + device: Box, tx: oneshot::Sender<()>, }, } -pub type DeviceBox = Box; - #[derive(Clone)] pub struct DevicesHandle { tx: mpsc::Sender, @@ -113,7 +113,7 @@ impl DevicesHandle { Ok(rx.await??) } - pub async fn add_device(&self, device: DeviceBox) -> Result<(), DevicesError> { + pub async fn add_device(&self, device: Box) -> Result<(), DevicesError> { let (tx, rx) = oneshot::channel(); self.tx.send(Command::AddDevice { device, tx }).await?; Ok(rx.await?) @@ -176,7 +176,7 @@ impl Devices { } } - fn add_device(&mut self, device: DeviceBox) { + fn add_device(&mut self, device: Box) { debug!(id = device.get_id(), "Adding device"); self.devices.insert(device.get_id().to_owned(), device); } diff --git a/src/devices/audio_setup.rs b/src/devices/audio_setup.rs index 22fa805..9ed9e08 100644 --- a/src/devices/audio_setup.rs +++ b/src/devices/audio_setup.rs @@ -8,7 +8,7 @@ use crate::error::DeviceError; use crate::mqtt::{OnMqtt, RemoteAction, RemoteMessage}; use crate::presence::OnPresence; -use super::{AsOnOff, Device, DeviceBox}; +use super::{As, Device}; // TODO: Ideally we store am Arc to the childern devices, // that way they hook into everything just like all other devices @@ -16,25 +16,25 @@ use super::{AsOnOff, Device, DeviceBox}; pub struct AudioSetup { identifier: String, mqtt: MqttDeviceConfig, - mixer: Box, - speakers: Box, + mixer: Box, + speakers: Box, } impl AudioSetup { pub async fn build( identifier: &str, mqtt: MqttDeviceConfig, - mixer: DeviceBox, - speakers: DeviceBox, + mixer: Box, + speakers: Box, client: AsyncClient, ) -> Result { // We expect the children devices to implement the OnOff trait let mixer_id = mixer.get_id().to_owned(); - let mixer = AsOnOff::consume(mixer).ok_or_else(|| DeviceError::OnOffExpected(mixer_id))?; + let mixer = As::consume(mixer).ok_or_else(|| DeviceError::OnOffExpected(mixer_id))?; let speakers_id = speakers.get_id().to_owned(); let speakers = - AsOnOff::consume(speakers).ok_or_else(|| DeviceError::OnOffExpected(speakers_id))?; + As::consume(speakers).ok_or_else(|| DeviceError::OnOffExpected(speakers_id))?; client .subscribe(mqtt.topic.clone(), rumqttc::QoS::AtLeastOnce) diff --git a/src/light_sensor.rs b/src/light_sensor.rs index 0201fa9..3eb52f5 100644 --- a/src/light_sensor.rs +++ b/src/light_sensor.rs @@ -10,7 +10,7 @@ use crate::{ }; #[async_trait] -pub trait OnDarkness { +pub trait OnDarkness: Sync + Send + 'static { async fn on_darkness(&mut self, dark: bool); } diff --git a/src/mqtt.rs b/src/mqtt.rs index 2495666..cd0134b 100644 --- a/src/mqtt.rs +++ b/src/mqtt.rs @@ -10,7 +10,7 @@ use rumqttc::{Event, EventLoop, Incoming, Publish}; use tokio::sync::broadcast; #[async_trait] -pub trait OnMqtt { +pub trait OnMqtt: Sync + Send + 'static { async fn on_mqtt(&mut self, message: &Publish); } diff --git a/src/presence.rs b/src/presence.rs index 03bdd53..f8b6cfa 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -12,7 +12,7 @@ use crate::{ }; #[async_trait] -pub trait OnPresence { +pub trait OnPresence: Sync + Send + 'static { async fn on_presence(&mut self, presence: bool); }