1use crate::{
2 attribute, dialect,
3 meta::v1::{CommonMetadata, CommonMetadataMut, ScopedMetadata},
4 serde::{is_default, Base64Standard},
5 translator, Dialect, Section, Translator,
6};
7use chrono::{DateTime, Utc};
8use core::fmt::{self, Formatter};
9use serde::{de::MapAccess, Deserialize, Deserializer, Serialize};
10use serde_json::{Map, Value};
11use std::{cmp::Ordering, collections::HashMap};
12
13#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
15pub struct Device {
16 pub metadata: ScopedMetadata,
17 #[serde(default)]
18 #[serde(skip_serializing_if = "Map::is_empty")]
19 pub spec: Map<String, Value>,
20 #[serde(default)]
21 #[serde(skip_serializing_if = "Map::is_empty")]
22 pub status: Map<String, Value>,
23}
24
25translator!(Device);
26
27impl AsRef<dyn CommonMetadata> for Device {
28 fn as_ref(&self) -> &(dyn CommonMetadata + 'static) {
29 &self.metadata
30 }
31}
32
33impl AsMut<dyn CommonMetadataMut> for Device {
34 fn as_mut(&mut self) -> &mut (dyn CommonMetadataMut + 'static) {
35 &mut self.metadata
36 }
37}
38
39impl Device {
40 pub fn validate_device(&self) -> bool {
42 match self.section::<DeviceSpecCore>() {
43 Some(Ok(core)) => {
45 if core.disabled {
46 return false;
47 }
48 }
49 Some(Err(_)) => {
51 return false;
52 }
53 _ => {}
55 };
56
57 true
59 }
60
61 pub fn new<A, D>(application: A, device: D) -> Self
63 where
64 A: AsRef<str>,
65 D: AsRef<str>,
66 {
67 Device {
68 metadata: ScopedMetadata {
69 application: application.as_ref().into(),
70 name: device.as_ref().into(),
71 ..Default::default()
72 },
73 ..Default::default()
74 }
75 }
76
77 pub fn add_credential(&mut self, credential: Credential) -> Result<(), serde_json::Error> {
81 self.update_section::<DeviceSpecCredentials, _>(|mut auth| {
83 auth.credentials.push(credential.clone());
84 auth
85 })?;
86 self.update_section::<DeviceSpecAuthentication, _>(|mut auth| {
87 auth.credentials.push(credential);
88 auth
89 })
90 }
91
92 pub fn get_credentials(&self) -> Option<Vec<Credential>> {
94 let credentials = match self.section::<DeviceSpecAuthentication>() {
95 Some(Ok(auth)) => auth.credentials,
96 _ => match self.section::<DeviceSpecCredentials>() {
97 Some(Ok(credentials)) => credentials.credentials,
98 _ => {
99 return None;
100 }
101 },
102 };
103 Some(credentials)
104 }
105}
106
107#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
108pub struct DeviceSpecCore {
109 #[serde(default)]
110 #[serde(skip_serializing_if = "is_default")]
111 pub disabled: bool,
112}
113
114attribute!(pub DeviceSpecCore[DeviceEnabled:bool] => |core| match core {
115 Some(Ok(core)) => core.disabled,
116 Some(Err(_)) => false,
118 None => true,
120});
121attribute!(pub DeviceSpecCommands[Commands:Vec<Command>] => |commands| match commands {
122 Some(Ok(commands)) => commands.commands,
123 _ => vec![],
124});
125attribute!(pub DeviceSpecCommands[FirstCommand:Option<Command>] => |commands| match commands {
126 Some(Ok(commands)) => commands.commands.get(0).cloned(),
127 _ => None,
128});
129
130impl Dialect for DeviceSpecCore {
131 fn key() -> &'static str {
132 "core"
133 }
134 fn section() -> Section {
135 Section::Spec
136 }
137}
138
139#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
141pub struct DeviceSpecCredentials {
142 #[serde(default)]
143 #[serde(skip_serializing_if = "Vec::is_empty")]
144 pub credentials: Vec<Credential>,
145}
146
147impl Dialect for DeviceSpecCredentials {
148 fn key() -> &'static str {
149 "credentials"
150 }
151 fn section() -> Section {
152 Section::Spec
153 }
154}
155
156#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
158pub enum Credential {
159 #[serde(rename = "user")]
160 UsernamePassword {
161 username: String,
162 password: Password,
163 #[serde(default)]
164 unique: bool,
165 },
166 #[serde(rename = "pass")]
167 Password(Password),
168 #[serde(rename = "cert")]
169 Certificate(String),
170 #[serde(rename = "psk")]
171 PreSharedKey(PreSharedKey),
172}
173
174#[derive(Clone, Serialize, PartialEq, Eq)]
175pub enum Password {
176 #[serde(rename = "plain")]
177 Plain(String),
178 #[serde(rename = "bcrypt")]
179 BCrypt(String),
180 #[serde(rename = "sha512")]
181 Sha512(String),
182}
183
184#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
186pub struct DeviceSpecAuthentication {
187 #[serde(default)]
188 #[serde(skip_serializing_if = "Vec::is_empty")]
189 pub credentials: Vec<Credential>,
190}
191
192impl Dialect for DeviceSpecAuthentication {
193 fn key() -> &'static str {
194 "authentication"
195 }
196 fn section() -> Section {
197 Section::Spec
198 }
199}
200
201#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
202pub struct PreSharedKey {
203 #[serde(with = "Base64Standard")]
204 pub key: Vec<u8>,
205 #[serde(skip_serializing_if = "Option::is_none")]
206 pub validity: Option<Validity>,
207}
208
209impl fmt::Debug for PreSharedKey {
210 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
211 write!(f, "key=..., validity: {:?}", self.validity)
212 }
213}
214
215impl PartialOrd for PreSharedKey {
216 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
217 if self.validity.is_none() && other.validity.is_none() {
218 Some(Ordering::Equal)
219 } else if self.validity.is_none() {
220 Some(Ordering::Less)
221 } else if other.validity.is_none() {
222 Some(Ordering::Equal)
223 } else {
224 self.validity.partial_cmp(&other.validity)
225 }
226 }
227}
228
229impl Ord for PreSharedKey {
230 fn cmp(&self, other: &Self) -> Ordering {
231 self.partial_cmp(other).unwrap()
233 }
234}
235
236#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)]
237pub struct Validity {
238 #[serde(rename = "notBefore")]
239 pub not_before: DateTime<Utc>,
240 #[serde(rename = "notAfter")]
241 pub not_after: DateTime<Utc>,
242}
243
244impl Validity {
245 pub fn is_valid(&self, now: DateTime<Utc>) -> bool {
246 self.not_before <= now && self.not_after >= now
247 }
248}
249
250impl PartialOrd for Validity {
251 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
252 Some(if self.not_before < other.not_before {
253 Ordering::Less
254 } else if self.not_before > other.not_before {
255 Ordering::Greater
256 } else if self.not_after > other.not_after {
257 Ordering::Less
258 } else if self.not_after < other.not_after {
259 Ordering::Greater
260 } else {
261 Ordering::Equal
262 })
263 }
264}
265
266impl Ord for Validity {
267 fn cmp(&self, other: &Self) -> Ordering {
268 self.partial_cmp(other).unwrap()
270 }
271}
272
273impl<'de> Deserialize<'de> for Password {
274 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
275 where
276 D: Deserializer<'de>,
277 {
278 deserializer.deserialize_any(PasswordVisitor)
279 }
280}
281
282struct PasswordVisitor;
283
284impl<'de> serde::de::Visitor<'de> for PasswordVisitor {
285 type Value = Password;
286
287 fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
288 formatter.write_str("A password, by string or map")
289 }
290
291 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
292 Ok(Password::Plain(value.to_owned()))
293 }
294
295 fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
296 Ok(Password::Plain(value))
297 }
298
299 fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
300 where
301 V: MapAccess<'de>,
302 {
303 if let Some(key) = map.next_key::<String>()? {
304 match key.as_str() {
305 "plain" => Ok(Password::Plain(map.next_value()?)),
306 "bcrypt" => Ok(Password::BCrypt(map.next_value()?)),
307 "sha512" => Ok(Password::Sha512(map.next_value()?)),
308 key => Err(serde::de::Error::unknown_field(
309 key,
310 &["plain", "bcrypt", "sha512"],
311 )),
312 }
313 } else {
314 Err(serde::de::Error::invalid_length(
315 0,
316 &"Expected exactly one field",
317 ))
318 }
319 }
320}
321
322impl fmt::Debug for Password {
323 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
324 f.write_str("...")
325 }
326}
327
328#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
329#[serde(rename_all = "camelCase")]
330pub struct DeviceSpecGatewaySelector {
331 #[serde(default)]
332 #[serde(skip_serializing_if = "Vec::is_empty")]
333 pub match_names: Vec<String>,
334}
335
336impl Dialect for DeviceSpecGatewaySelector {
337 fn key() -> &'static str {
338 "gatewaySelector"
339 }
340 fn section() -> Section {
341 Section::Spec
342 }
343}
344
345#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
346pub struct DeviceSpecCommands {
347 #[serde(default)]
348 #[serde(skip_serializing_if = "Vec::is_empty")]
349 pub commands: Vec<Command>,
350}
351
352impl Dialect for DeviceSpecCommands {
353 fn key() -> &'static str {
354 "commands"
355 }
356 fn section() -> Section {
357 Section::Spec
358 }
359}
360
361#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
362pub enum Command {
363 #[serde(rename = "external")]
364 External(ExternalCommandEndpoint),
365}
366
367#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
368pub struct ExternalCommandEndpoint {
369 pub r#type: Option<String>,
370 pub url: String,
371 #[serde(default, skip_serializing_if = "String::is_empty")]
372 pub method: String,
373 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
374 pub headers: HashMap<String, String>,
375}
376
377#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]
378pub struct DeviceSpecAliases(
379 #[serde(default)]
380 #[serde(skip_serializing_if = "Vec::is_empty")]
381 pub Vec<String>,
382);
383
384dialect!(DeviceSpecAliases [Section::Spec => "alias"]);
385
386#[cfg(test)]
387mod test;