2023-10-23 08:03:16 +00:00
|
|
|
use core::slice::Iter;
|
|
|
|
use std::{fmt::Display, str::FromStr};
|
|
|
|
|
|
|
|
use anyhow::{bail, Result};
|
2023-10-23 11:30:45 +00:00
|
|
|
use serde::Serialize;
|
2023-10-23 08:03:16 +00:00
|
|
|
|
2023-10-23 11:30:45 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)]
|
2023-10-23 08:03:16 +00:00
|
|
|
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<Self, Self::Err> {
|
|
|
|
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}"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 09:07:37 +00:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
|
|
pub struct ActionID(pub(crate) i64);
|
|
|
|
|
2023-10-30 05:49:59 +00:00
|
|
|
#[derive(Debug, Clone, Eq, PartialOrd, Ord, Serialize)]
|
2023-10-23 08:03:16 +00:00
|
|
|
pub struct Action {
|
2023-10-23 11:30:45 +00:00
|
|
|
#[serde(skip)]
|
2023-10-23 09:07:37 +00:00
|
|
|
pub(crate) id: Option<ActionID>,
|
|
|
|
|
2023-10-23 08:03:16 +00:00
|
|
|
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 {
|
2023-10-23 09:07:37 +00:00
|
|
|
id: None,
|
|
|
|
|
2023-10-23 08:03:16 +00:00
|
|
|
device_id: device_id.to_string(),
|
|
|
|
action_type,
|
|
|
|
parameter: parameter.to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-30 05:49:59 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 11:30:45 +00:00
|
|
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize)]
|
2023-10-23 08:03:16 +00:00
|
|
|
pub struct ActionSet {
|
|
|
|
actions: Vec<Action>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ActionSet {
|
2023-10-23 08:32:52 +00:00
|
|
|
pub fn push_device(&self) -> Option<String> {
|
|
|
|
self.iter()
|
|
|
|
.find(|action| action.action_type == ActionType::Push)
|
|
|
|
.map(|action| action.device_id.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn receive_device(&self) -> Option<String> {
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
2023-10-23 09:07:37 +00:00
|
|
|
|
|
|
|
pub(crate) fn first_id(&self) -> Option<ActionID> {
|
|
|
|
self.actions.get(0).map(|action| action.id).flatten()
|
|
|
|
}
|
2023-10-23 08:32:52 +00:00
|
|
|
|
|
|
|
pub fn parameter(&self, parameter: &str) -> bool {
|
|
|
|
match self.actions.get(0) {
|
|
|
|
Some(action) => action.parameter == parameter,
|
|
|
|
None => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-23 08:03:16 +00:00
|
|
|
pub fn chain(&mut self, action: Action) {
|
|
|
|
self.actions.push(action);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn iter(&self) -> Iter<'_, Action> {
|
|
|
|
self.actions.iter()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<I> From<I> for ActionSet
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = Action>,
|
|
|
|
{
|
|
|
|
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(())
|
|
|
|
}
|
|
|
|
}
|