enet_proto/
res.rs

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}