use core::slice::Iter; use std::{fmt::Display, str::FromStr}; use anyhow::{bail, Result}; use serde::Serialize; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)] pub enum ActionType { GreaterThan, LessThan, Push, Receive, Update, } impl Display for ActionType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::GreaterThan => write!(f, "GreaterThan"), Self::LessThan => write!(f, "LessThan"), Self::Push => write!(f, "Push"), Self::Receive => write!(f, "Receive"), Self::Update => write!(f, "Update"), } } } impl FromStr for ActionType { type Err = anyhow::Error; fn from_str(s: &str) -> Result { match s { "GreaterThan" => Ok(Self::GreaterThan), "LessThan" => Ok(Self::LessThan), "Push" => Ok(Self::Push), "Receive" => Ok(Self::Receive), "Update" => Ok(Self::Update), _ => bail!("could not parse ActionType from {s}"), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct ActionID(pub(crate) i64); #[derive(Debug, Clone, Eq, PartialOrd, Ord, Serialize)] pub struct Action { #[serde(skip)] pub(crate) id: Option, pub device_id: String, pub action_type: ActionType, pub parameter: String, } impl Action { pub fn new( device_id: impl ToString, action_type: ActionType, parameter: impl ToString, ) -> Self { Self { id: None, device_id: device_id.to_string(), action_type, parameter: parameter.to_string(), } } } impl PartialEq for Action { fn eq(&self, other: &Self) -> bool { let id_comp = match (self.id, other.id) { (Some(self_id), Some(other_id)) => self_id == other_id, _ => true, }; id_comp && self.device_id == other.device_id && self.action_type == other.action_type && self.parameter == other.parameter } } #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)] pub struct ActionSet { actions: Vec, } impl ActionSet { pub fn push_device(&self) -> Option { self.iter() .find(|action| action.action_type == ActionType::Push) .map(|action| action.device_id.clone()) } pub fn receive_device(&self) -> Option { self.iter() .find(|action| action.action_type == ActionType::Receive) .map(|action| action.device_id.clone()) } pub fn begins_with_device(&self, device_name: &str) -> bool { match self.actions.get(0) { Some(action) => action.device_id == device_name, None => false, } } pub(crate) fn first_id(&self) -> Option { self.actions.get(0).map(|action| action.id).flatten() } pub fn parameter(&self, parameter: &str) -> bool { match self.actions.get(0) { Some(action) => action.parameter == parameter, None => false, } } pub fn chain(&mut self, action: Action) { self.actions.push(action); } pub fn iter(&self) -> Iter<'_, Action> { self.actions.iter() } } impl From for ActionSet where I: IntoIterator, { fn from(value: I) -> Self { Self { actions: value.into_iter().collect(), } } } #[cfg(test)] mod test { use super::*; use anyhow::Result; #[test] fn example_chain() -> Result<()> { let mut action_set = ActionSet::default(); action_set.chain(Action::new( "shelly_plus_ht", ActionType::Push, "temperature", )); action_set.chain(Action::new( "shelly_trv", ActionType::Receive, "temperature", )); Ok(()) } }