Replaced impl_cast with a new and improved trait
All checks were successful
Build and deploy automation_rs / Build automation_rs (push) Successful in 4m0s
Build and deploy automation_rs / Build Docker image (push) Successful in 52s
Build and deploy automation_rs / Deploy Docker container (push) Successful in 28s

With this trait the impl_cast macros are no longer needed, simplifying
everything.
This commit also improved how the actual casting itself is handled.
This commit is contained in:
2024-05-05 00:33:21 +02:00
parent cde9654a78
commit 3689a52afd
16 changed files with 174 additions and 332 deletions

View File

@@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
impl_cast = { path = "../impl_cast" }
automation_cast = { path = "../automation_cast/" }
serde = { version = "1.0.149", features = ["derive"] }
serde_json = "1.0.89"
thiserror = "1.0.37"

View File

@@ -1,4 +1,5 @@
use async_trait::async_trait;
use automation_cast::Cast;
use serde::Serialize;
use crate::errors::{DeviceError, ErrorCode};
@@ -7,43 +8,10 @@ use crate::response;
use crate::traits::{FanSpeed, HumiditySetting, OnOff, Scene, Trait};
use crate::types::Type;
// TODO: Find a more elegant way to do this
pub trait AsGoogleHomeDevice {
fn cast(&self) -> Option<&dyn GoogleHomeDevice>;
fn cast_mut(&mut self) -> Option<&mut dyn GoogleHomeDevice>;
}
// Default impl
impl<T> AsGoogleHomeDevice for T
where
T: 'static,
{
default fn cast(&self) -> Option<&(dyn GoogleHomeDevice + 'static)> {
None
}
default fn cast_mut(&mut self) -> Option<&mut (dyn GoogleHomeDevice + 'static)> {
None
}
}
// Specialization
impl<T> AsGoogleHomeDevice for T
where
T: GoogleHomeDevice + 'static,
{
fn cast(&self) -> Option<&(dyn GoogleHomeDevice + 'static)> {
Some(self)
}
fn cast_mut(&mut self) -> Option<&mut (dyn GoogleHomeDevice + 'static)> {
Some(self)
}
}
#[async_trait]
#[impl_cast::device(As: OnOff + Scene + FanSpeed + HumiditySetting)]
pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static {
pub trait GoogleHomeDevice:
Sync + Send + Cast<dyn OnOff> + Cast<dyn Scene> + Cast<dyn FanSpeed> + Cast<dyn HumiditySetting>
{
fn get_device_type(&self) -> Type;
fn get_device_name(&self) -> Name;
fn get_id(&self) -> &str;
@@ -76,26 +44,26 @@ pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static {
let mut traits = Vec::new();
// OnOff
if let Some(on_off) = As::<dyn OnOff>::cast(self) {
if let Some(on_off) = self.cast() as Option<&dyn OnOff> {
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) = As::<dyn Scene>::cast(self) {
if let Some(scene) = self.cast() as Option<&dyn Scene> {
traits.push(Trait::Scene);
device.attributes.scene_reversible = scene.is_scene_reversible();
}
// FanSpeed
if let Some(fan_speed) = As::<dyn FanSpeed>::cast(self) {
if let Some(fan_speed) = self.cast() as Option<&dyn FanSpeed> {
traits.push(Trait::FanSpeed);
device.attributes.command_only_fan_speed = fan_speed.command_only_fan_speed();
device.attributes.available_fan_speeds = Some(fan_speed.available_speeds());
}
if let Some(humidity_setting) = As::<dyn HumiditySetting>::cast(self) {
if let Some(humidity_setting) = self.cast() as Option<&dyn HumiditySetting> {
traits.push(Trait::HumiditySetting);
device.attributes.query_only_humidity_setting =
humidity_setting.query_only_humidity_setting();
@@ -113,7 +81,7 @@ pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static {
}
// OnOff
if let Some(on_off) = As::<dyn OnOff>::cast(self) {
if let Some(on_off) = self.cast() as Option<&dyn OnOff> {
device.state.on = on_off
.is_on()
.await
@@ -122,11 +90,11 @@ pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static {
}
// FanSpeed
if let Some(fan_speed) = As::<dyn FanSpeed>::cast(self) {
if let Some(fan_speed) = self.cast() as Option<&dyn FanSpeed> {
device.state.current_fan_speed_setting = Some(fan_speed.current_speed().await);
}
if let Some(humidity_setting) = As::<dyn HumiditySetting>::cast(self) {
if let Some(humidity_setting) = self.cast() as Option<&dyn HumiditySetting> {
device.state.humidity_ambient_percent =
Some(humidity_setting.humidity_ambient_percent().await);
}
@@ -137,21 +105,21 @@ pub trait GoogleHomeDevice: AsGoogleHomeDevice + Sync + Send + 'static {
async fn execute(&mut self, command: &CommandType) -> Result<(), ErrorCode> {
match command {
CommandType::OnOff { on } => {
if let Some(t) = As::<dyn OnOff>::cast_mut(self) {
if let Some(t) = self.cast_mut() as Option<&mut dyn OnOff> {
t.set_on(*on).await?;
} else {
return Err(DeviceError::ActionNotAvailable.into());
}
}
CommandType::ActivateScene { deactivate } => {
if let Some(t) = As::<dyn Scene>::cast(self) {
if let Some(t) = self.cast_mut() as Option<&mut dyn Scene> {
t.set_active(!deactivate).await?;
} else {
return Err(DeviceError::ActionNotAvailable.into());
}
}
CommandType::SetFanSpeed { fan_speed } => {
if let Some(t) = As::<dyn FanSpeed>::cast(self) {
if let Some(t) = self.cast_mut() as Option<&mut dyn FanSpeed> {
t.set_speed(fan_speed).await?;
}
}

View File

@@ -1,14 +1,15 @@
use std::collections::HashMap;
use std::sync::Arc;
use automation_cast::Cast;
use futures::future::{join_all, OptionFuture};
use thiserror::Error;
use tokio::sync::{Mutex, RwLock};
use crate::device::AsGoogleHomeDevice;
use crate::errors::{DeviceError, ErrorCode};
use crate::request::{self, Intent, Request};
use crate::response::{self, execute, query, sync, Response, ResponsePayload, State};
use crate::GoogleHomeDevice;
#[derive(Debug)]
pub struct GoogleHome {
@@ -29,7 +30,7 @@ impl GoogleHome {
}
}
pub async fn handle_request<T: AsGoogleHomeDevice + ?Sized + 'static>(
pub async fn handle_request<T: Cast<dyn GoogleHomeDevice> + ?Sized + 'static>(
&self,
request: Request,
devices: &HashMap<String, Arc<RwLock<Box<T>>>>,
@@ -58,7 +59,7 @@ impl GoogleHome {
.map(|payload| Response::new(&request.request_id, payload))
}
async fn sync<T: AsGoogleHomeDevice + ?Sized + 'static>(
async fn sync<T: Cast<dyn GoogleHomeDevice> + ?Sized + 'static>(
&self,
devices: &HashMap<String, Arc<RwLock<Box<T>>>>,
) -> sync::Payload {
@@ -75,7 +76,7 @@ impl GoogleHome {
resp_payload
}
async fn query<T: AsGoogleHomeDevice + ?Sized + 'static>(
async fn query<T: Cast<dyn GoogleHomeDevice> + ?Sized + 'static>(
&self,
payload: request::query::Payload,
devices: &HashMap<String, Arc<RwLock<Box<T>>>>,
@@ -107,7 +108,7 @@ impl GoogleHome {
resp_payload
}
async fn execute<T: AsGoogleHomeDevice + ?Sized + 'static>(
async fn execute<T: Cast<dyn GoogleHomeDevice> + ?Sized + 'static>(
&self,
payload: request::execute::Payload,
devices: &HashMap<String, Arc<RwLock<Box<T>>>>,

View File

@@ -16,8 +16,7 @@ pub enum Trait {
}
#[async_trait]
#[impl_cast::device_trait]
pub trait OnOff {
pub trait OnOff: Sync + Send {
fn is_command_only(&self) -> Option<bool> {
None
}
@@ -32,8 +31,7 @@ pub trait OnOff {
}
#[async_trait]
#[impl_cast::device_trait]
pub trait Scene {
pub trait Scene: Sync + Send {
fn is_scene_reversible(&self) -> Option<bool> {
None
}
@@ -60,8 +58,7 @@ pub struct AvailableSpeeds {
}
#[async_trait]
#[impl_cast::device_trait]
pub trait FanSpeed {
pub trait FanSpeed: Sync + Send {
fn reversible(&self) -> Option<bool> {
None
}
@@ -76,8 +73,7 @@ pub trait FanSpeed {
}
#[async_trait]
#[impl_cast::device_trait]
pub trait HumiditySetting {
pub trait HumiditySetting: Sync + Send {
// TODO: This implementation is not complete, I have only implemented what I need right now
fn query_only_humidity_setting(&self) -> Option<bool> {
None