1mod de;
2mod proj;
3mod update;
4
5use crate::ProtocolVersion;
6use derive_more::{From, IsVariant};
7use enum_kinds::EnumKind;
8use serde::{Deserialize, Deserializer};
9use serde_json::Value;
10use std::convert::TryFrom;
11use tracing::{event, Level};
12
13pub use proj::*;
14pub use update::ItemUpdateValue;
15
16const FIELD_NAME_PROTOCOL: &str = "PROTOCOL";
17const FIELD_NAME_KIND: &str = "CMD";
18
19mod sealed {
20 pub trait Sealed {}
21}
22
23pub trait ResponseType: Into<Response> + TryFrom<Response> + sealed::Sealed {
24 fn protocol_version(&self) -> ProtocolVersion;
25}
26
27macro_rules! impl_response_type {
28 ($t:ty => $e:expr) => {
29 impl sealed::Sealed for $t {}
30 impl ResponseType for $t {
31 #[inline]
32 fn protocol_version(&self) -> ProtocolVersion {
33 $e
34 }
35 }
36
37 impl $t {
38 fn deserialize_response<'de, D>(deserializer: D) -> Result<Response, D::Error>
39 where
40 D: Deserializer<'de>
41 {
42 <Self as Deserialize<'de>>::deserialize(deserializer).map(Into::into)
43 }
44 }
45 };
46}
47
48#[derive(Debug, Deserialize)]
49#[serde(rename_all = "UPPERCASE")]
50pub struct VersionRes {
51 pub firmware: String,
52 pub hardware: String,
53 pub enet: String,
54}
55impl_response_type!(VersionRes => ProtocolVersion::ZeroZeroThree);
56
57#[derive(Debug, Deserialize)]
58#[serde(rename_all = "UPPERCASE")]
59pub struct GetChannelInfoAllRes {
60 pub devices: Vec<u32>,
61}
62impl_response_type!(GetChannelInfoAllRes => ProtocolVersion::ZeroZeroThree);
63
64#[derive(Debug, Deserialize)]
65#[serde(rename_all = "UPPERCASE")]
66pub struct ItemValueRes {}
67impl_response_type!(ItemValueRes => ProtocolVersion::ZeroZeroThree);
68
69#[derive(Debug, Deserialize)]
70#[serde(rename_all = "UPPERCASE")]
71pub struct ItemValueSignInRes {}
72impl_response_type!(ItemValueSignInRes => ProtocolVersion::ZeroZeroThree);
73
74#[derive(Debug, Deserialize)]
75#[serde(rename_all = "UPPERCASE")]
76pub struct ProjectListRes {
77 pub project_id: String,
78 pub items: Vec<proj::ProjectItem>,
79 pub lists: Vec<proj::ProjectList>,
80}
81impl_response_type!(ProjectListRes => ProtocolVersion::ZeroZeroThree);
82
83#[derive(Debug, Deserialize)]
84#[serde(rename_all = "UPPERCASE")]
85pub struct ItemUpdateInd {
86 pub values: Vec<ItemUpdateValue>,
87}
88impl_response_type!(ItemUpdateInd => ProtocolVersion::ZeroZeroThree);
89
90#[derive(Debug)]
91pub struct UnknownRes {
92 pub kind: String,
93 pub protocol: String,
94 pub values: Value,
95}
96
97#[derive(Debug, From, IsVariant, EnumKind)]
98#[enum_kind(ResponseKind)]
99pub enum Response {
100 Version(VersionRes),
101
102 GetChannelInfoAll(GetChannelInfoAllRes),
103
104 ItemValueSignIn(ItemValueSignInRes),
105
106 ItemValue(ItemValueRes),
107
108 ProjectList(ProjectListRes),
109
110 ItemUpdate(ItemUpdateInd),
111
112 Unknown(UnknownRes),
113}
114
115macro_rules! try_into {
116 ($variant:ident => $ty:ty) => {
117 impl TryFrom<Response> for $ty {
118 type Error = Response;
119
120 #[inline]
121 fn try_from(value: Response) -> Result<Self, Self::Error> {
122 match value {
123 Response::$variant(v) => Ok(v),
124 _ => Err(value),
125 }
126 }
127 }
128 };
129}
130
131try_into!(Version => VersionRes);
132try_into!(GetChannelInfoAll => GetChannelInfoAllRes);
133try_into!(ProjectList => ProjectListRes);
134try_into!(ItemUpdate => ItemUpdateInd);
135try_into!(ItemValue => ItemValueRes);
136try_into!(ItemValueSignIn => ItemValueSignInRes);
137
138impl Response {
139 #[inline]
140 pub fn kind(&self) -> ResponseKind {
141 ResponseKind::from(self)
142 }
143}
144
145macro_rules! match_response {
146 ($kind:ident, $protocol:ident, $deserializer:ident => {
147 $(($k:ident, $v:ident) => $t:ty),*$(,)?
148 }) => {
149 match (&$kind, &$protocol) {
150 $(
151 (de::ResponseKind::$k, ProtocolVersion::$v) => <$t>::deserialize_response($deserializer),
152 )+
153 (de::ResponseKind::Unknown(_), _) | (_, ProtocolVersion::Unknown(_)) => {
154 reconstruct($kind, $protocol, $deserializer)
155 }
156 }
157 };
158}
159
160impl Response {
161 fn deserialize<'de, D>(
162 kind: de::ResponseKind,
163 protocol: ProtocolVersion,
164 deserializer: D,
165 ) -> Result<Self, D::Error>
166 where
167 D: Deserializer<'de>,
168 {
169 match_response!(kind, protocol, deserializer => {
170 (Version, ZeroZeroThree) => VersionRes,
171 (GetChannelInfoAll, ZeroZeroThree) => GetChannelInfoAllRes,
172 (ItemValue, ZeroZeroThree) => ItemValueRes,
173 (ItemValueSignIn, ZeroZeroThree) => ItemValueSignInRes,
174 (ProjectList, ZeroZeroThree) => ProjectListRes,
175 (ItemUpdate, ZeroZeroThree) => ItemUpdateInd,
176 })
177 }
178}
179
180fn reconstruct<'de, D>(
181 kind: de::ResponseKind,
182 protocol: ProtocolVersion,
183 deserializer: D,
184) -> Result<Response, D::Error>
185where
186 D: Deserializer<'de>,
187{
188 let json = serde_json::Value::deserialize(deserializer)?;
189
190 event!(target: "enet-proto::res", Level::WARN, %kind, %protocol, %json, "received unknown response type");
191 Ok(Response::Unknown(UnknownRes {
192 kind: kind.to_string(),
193 protocol: protocol.to_string(),
194 values: json,
195 }))
196}