Implemented data structures for SYNC, QUERY, and EXECUTE

This commit is contained in:
2022-12-13 05:38:21 +01:00
parent 6627174c6f
commit e4369ebf41
8 changed files with 499 additions and 67 deletions

View File

@@ -0,0 +1,98 @@
use serde::Serialize;
use crate::response::State;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Payload {
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub debug_string: Option<String>,
commands: Vec<Command>,
}
impl Payload {
pub fn new() -> Self {
Self { error_code: None, debug_string: None, commands: Vec::new() }
}
pub fn add_command(&mut self, command: Command) {
self.commands.push(command);
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Command {
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
ids: Vec<String>,
status: Status,
pub states: Option<States>,
}
impl Command {
pub fn new(status: Status) -> Self {
Self { error_code: None, ids: Vec::new(), status, states: None }
}
pub fn add_id(&mut self, id: &str) {
self.ids.push(id.into());
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct States {
pub online: bool,
#[serde(flatten)]
pub state: Option<State>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Status {
Success,
Pending,
Offline,
Exceptions,
Error,
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use uuid::Uuid;
use super::*;
use crate::response::{Response, ResponsePayload, State};
#[test]
fn serialize() {
let mut execute_resp = Payload::new();
let state = State::default().on(true);
let mut command = Command::new(Status::Success);
command.states = Some(States {
online: true,
state: Some(state)
});
command.ids.push("123".into());
execute_resp.add_command(command);
let mut command = Command::new(Status::Error);
command.error_code = Some("deviceTurnedOff".into());
command.ids.push("456".into());
execute_resp.add_command(command);
let resp = Response::new(Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), ResponsePayload::Execute(execute_resp));
let json = serde_json::to_string(&resp).unwrap();
println!("{}", json);
// @TODO Add a known correct output to test against
}
}

View File

@@ -0,0 +1,83 @@
use std::collections::HashMap;
use serde::Serialize;
use crate::response::State;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Payload {
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub debug_string: Option<String>,
devices: HashMap<String, Device>,
}
impl Payload {
pub fn new() -> Self {
Self { error_code: None, debug_string: None, devices: HashMap::new() }
}
pub fn add_device(&mut self, id: &str, device: Device) {
self.devices.insert(id.into(), device);
}
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Status {
Success,
Offline,
Exceptions,
Error,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Device {
online: bool,
status: Status,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
#[serde(flatten)]
pub state: Option<State>,
}
impl Device {
pub fn new(online: bool, status: Status) -> Self {
Self { online, status, error_code: None, state: None }
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use uuid::Uuid;
use super::*;
use crate::response::{Response, ResponsePayload, State};
#[test]
fn serialize() {
let mut query_resp = Payload::new();
let state = State::default().on(true);
let mut device = Device::new(true, Status::Success);
device.state = Some(state);
query_resp.add_device("123", device);
let state = State::default().on(false);
let mut device = Device::new(true, Status::Success);
device.state = Some(state);
query_resp.add_device("456", device);
let resp = Response::new(Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), ResponsePayload::Query(query_resp));
let json = serde_json::to_string(&resp).unwrap();
println!("{}", json);
// @TODO Add a known correct output to test against
}
}

View File

@@ -6,17 +6,17 @@ use crate::traits::Trait;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Payload {
user_agent_id: String,
agent_user_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
error_code: Option<String>,
pub error_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
debug_string: Option<String>,
devices: Vec<Device>,
pub debug_string: Option<String>,
pub devices: Vec<Device>,
}
impl Payload {
pub fn new(user_agent_id: &str) -> Self {
Self { user_agent_id: user_agent_id.into(), error_code: None, debug_string: None, devices: Vec::new() }
pub fn new(agent_user_id: &str) -> Self {
Self { agent_user_id: agent_user_id.into(), error_code: None, debug_string: None, devices: Vec::new() }
}
pub fn add_device(&mut self, device: Device) {
@@ -27,9 +27,9 @@ impl Payload {
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Device {
pub id: String,
id: String,
#[serde(rename = "type")]
pub device_type: Type,
device_type: Type,
pub traits: Vec<Trait>,
pub name: DeviceName,
pub will_report_state: bool,
@@ -37,6 +37,10 @@ pub struct Device {
pub notification_supported_by_agent: Option<bool>,
#[serde(skip_serializing_if = "String::is_empty")]
pub room_hint: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub device_info: Option<DeviceInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attributes: Option<Attributes>,
}
impl Device {
@@ -46,12 +50,14 @@ impl Device {
device_type,
traits: Vec::new(),
name: DeviceName {
default_name: Vec::new(),
default_names: Vec::new(),
name: name.into(),
nicknames: Vec::new() },
will_report_state: true,
will_report_state: false,
notification_supported_by_agent: None,
room_hint: "".into(),
device_info: None,
attributes: None,
}
}
}
@@ -60,8 +66,66 @@ impl Device {
#[serde(rename_all = "camelCase")]
pub struct DeviceName {
#[serde(skip_serializing_if = "Vec::is_empty")]
pub default_name: Vec<String>,
pub name: String,
pub default_names: Vec<String>,
name: String,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub nicknames: Vec<String>,
}
#[derive(Debug, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceInfo {
#[serde(skip_serializing_if = "String::is_empty")]
pub manufacturer: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub model: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub hw_version: String,
#[serde(skip_serializing_if = "String::is_empty")]
pub sw_version: String,
// attributes
// customData
// otherDeviceIds
}
#[derive(Debug, Default, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Attributes {
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use uuid::Uuid;
use super::*;
use crate::{response::{Response, ResponsePayload}, types::Type, traits::Trait};
#[test]
fn serialize() {
let mut sync_resp = Payload::new("1836.15267389");
let mut device = Device::new("123", "Night light", Type::Kettle);
device.traits.push(Trait::OnOff);
device.name.default_names.push("My Outlet 1234".to_string());
device.name.nicknames.push("wall plug".to_string());
device.room_hint = "kitchen".into();
device.device_info = Some(DeviceInfo {
manufacturer: "lights-out-inc".to_string(),
model: "hs1234".to_string(),
hw_version: "3.2".to_string(),
sw_version: "11.4".to_string(),
});
sync_resp.add_device(device);
let resp = Response::new(Uuid::from_str("ff36a3cc-ec34-11e6-b1a0-64510650abcf").unwrap(), ResponsePayload::Sync(sync_resp));
let json = serde_json::to_string(&resp).unwrap();
println!("{}", json);
assert_eq!(json, r#"{"requestId":"ff36a3cc-ec34-11e6-b1a0-64510650abcf","payload":{"agentUserId":"1836.15267389","devices":[{"id":"123","type":"action.devices.types.KETTLE","traits":["action.devices.traits.OnOff"],"name":{"defaultNames":["My Outlet 1234"],"name":"Night light","nicknames":["wall plug"]},"willReportState":false,"roomHint":"kitchen","deviceInfo":{"manufacturer":"lights-out-inc","model":"hs1234","hwVersion":"3.2","swVersion":"11.4"}}]}}"#)
}
}