1use derive_more::IsVariant;
2use enum_kinds::EnumKind;
3use serde::{
4 de::{Unexpected, Visitor},
5 Deserialize, Deserializer,
6};
7use std::fmt;
8
9pub trait EnetItem {
10 fn number(&self) -> u32;
11 fn name(&self) -> &str;
12 fn is_subscribable(&self) -> bool;
13}
14
15macro_rules! impl_item {
16 ($ty:ty : $id:ident => $sub:expr) => {
17 impl EnetItem for $ty {
18 #[inline]
19 fn number(&self) -> u32 {
20 self.number
21 }
22
23 #[inline]
24 fn name(&self) -> &str {
25 &self.name
26 }
27
28 #[inline]
29 fn is_subscribable(&self) -> bool {
30 let $id = self;
31 $sub
32 }
33 }
34 };
35
36 ($ty:ty => $sub:literal) => {
37 impl_item!($ty : _v => $sub);
38 }
39}
40
41#[derive(Debug, Deserialize, EnumKind, IsVariant)]
43#[serde(tag = "TYPE", rename_all = "UPPERCASE")]
44#[enum_kind(ProjectItemKind)]
45pub enum ProjectItem {
46 #[serde(alias = "Scene")]
47 Scene(ProjectScene),
48 Binaer(ProjectBinaer),
49 Dimmer(ProjectDimmer),
50 Jalousie(ProjectJalousie),
51 None(ProjectNone),
52}
53
54impl ProjectItem {
55 #[inline]
56 pub fn kind(&self) -> ProjectItemKind {
57 ProjectItemKind::from(self)
58 }
59}
60
61impl EnetItem for ProjectItem {
62 fn number(&self) -> u32 {
63 match self {
64 ProjectItem::Scene(v) => v.number(),
65 ProjectItem::Binaer(v) => v.number(),
66 ProjectItem::Dimmer(v) => v.number(),
67 ProjectItem::Jalousie(v) => v.number(),
68 ProjectItem::None(v) => v.number(),
69 }
70 }
71
72 fn name(&self) -> &str {
73 match self {
74 ProjectItem::Scene(v) => v.name(),
75 ProjectItem::Binaer(v) => v.name(),
76 ProjectItem::Dimmer(v) => v.name(),
77 ProjectItem::Jalousie(v) => v.name(),
78 ProjectItem::None(v) => v.name(),
79 }
80 }
81
82 fn is_subscribable(&self) -> bool {
83 match self {
84 ProjectItem::Scene(v) => v.is_subscribable(),
85 ProjectItem::Binaer(v) => v.is_subscribable(),
86 ProjectItem::Dimmer(v) => v.is_subscribable(),
87 ProjectItem::Jalousie(v) => v.is_subscribable(),
88 ProjectItem::None(v) => v.is_subscribable(),
89 }
90 }
91}
92
93#[derive(Debug, Deserialize)]
94#[serde(rename_all = "UPPERCASE")]
95pub struct ProjectScene {
96 pub number: u32,
97 pub name: String,
98 pub dimmable: bool,
99}
100impl_item!(ProjectScene => false);
101
102#[derive(Debug, Deserialize)]
103#[serde(rename_all = "UPPERCASE")]
104pub struct ProjectBinaer {
105 pub number: u32,
106 pub name: String,
107 #[serde(default = "get_true", deserialize_with = "deserialize_programmable")]
108 pub programmable: bool,
109}
110impl_item!(ProjectBinaer : v => v.programmable);
111
112#[derive(Debug, Deserialize)]
113#[serde(rename_all = "UPPERCASE")]
114pub struct ProjectDimmer {
115 pub number: u32,
116 pub name: String,
117}
118impl_item!(ProjectDimmer => true);
119
120#[derive(Debug, Deserialize)]
121#[serde(rename_all = "UPPERCASE")]
122pub struct ProjectJalousie {
123 pub number: u32,
124 pub name: String,
125}
126impl_item!(ProjectJalousie => true);
127
128#[derive(Debug, Deserialize)]
129#[serde(rename_all = "UPPERCASE")]
130pub struct ProjectNone {
131 pub number: u32,
132 pub name: String,
133}
134impl_item!(ProjectNone => false);
135
136#[derive(Debug, Deserialize)]
137#[serde(rename_all = "UPPERCASE")]
138pub struct ProjectList {
139 pub number: u32,
140 pub name: String,
141 #[serde(default)]
142 pub items_order: Vec<u32>,
143 pub visible: bool,
144}
145
146struct ProgrammableVisitor;
147
148impl<'de> Visitor<'de> for ProgrammableVisitor {
149 type Value = bool;
150
151 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
152 formatter.write_str("bool")
153 }
154
155 #[inline]
156 fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
157 where
158 E: serde::de::Error,
159 {
160 Ok(v)
161 }
162
163 #[inline]
164 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
165 where
166 E: serde::de::Error,
167 {
168 match v {
169 "false" | "FALSE" => Ok(false),
170 "true" | "TRUE" => Ok(true),
171 _ => Err(E::invalid_value(Unexpected::Str(v), &self)),
172 }
173 }
174}
175
176fn deserialize_programmable<'de, D>(serializer: D) -> Result<bool, D::Error>
177where
178 D: Deserializer<'de>,
179{
180 serializer.deserialize_any(ProgrammableVisitor)
181}
182
183#[inline]
184fn get_true() -> bool {
185 true
186}