use crate::resource;
use chrono::NaiveDateTime;
use derive_setters::Setters;
use serde::{Deserialize, Serialize};
use serde_json::{Error as JsonError, Value as JsonValue};
#[derive(Clone, Debug, Eq, PartialEq, Deserialize)]
pub struct Schedule {
#[serde(skip)]
pub id: String,
pub name: String,
pub description: String,
pub command: Command,
#[serde(rename = "localtime")]
pub local_time: String,
#[serde(rename = "starttime")]
pub start_time: Option<NaiveDateTime>,
pub status: Status,
#[serde(rename = "autodelete")]
pub auto_delete: Option<bool>,
}
impl Schedule {
pub(crate) fn with_id(self, id: String) -> Self {
Self { id, ..self }
}
}
impl resource::Resource for Schedule {}
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Command {
pub address: String,
#[serde(rename = "method")]
pub request_method: CommandRequestMethod,
pub body: JsonValue,
}
impl Command {
pub fn from_creator<C, S>(creator: &C, username: S) -> Result<Self, JsonError>
where
C: resource::Creator,
S: AsRef<str>,
{
Ok(Self {
address: format!("/api/{}/{}", username.as_ref(), C::url_suffix()),
request_method: CommandRequestMethod::Post,
body: serde_json::to_value(creator)?,
})
}
pub fn from_modifier<M, S>(modifier: &M, id: M::Id, username: S) -> Result<Self, JsonError>
where
M: resource::Modifier,
S: AsRef<str>,
{
Ok(Self {
address: format!("/api/{}/{}", username.as_ref(), M::url_suffix(id)),
request_method: CommandRequestMethod::Put,
body: serde_json::to_value(modifier)?,
})
}
pub fn from_scanner<T, S>(scanner: &T, username: S) -> Result<Self, JsonError>
where
T: resource::Scanner,
S: AsRef<str>,
{
Ok(Self {
address: format!("/api/{}/{}", username.as_ref(), T::url_suffix()),
request_method: CommandRequestMethod::Post,
body: serde_json::to_value(scanner)?,
})
}
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum CommandRequestMethod {
Put,
Post,
Delete,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Status {
Enabled,
Disabled,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Setters)]
#[setters(strip_option, prefix = "with_")]
pub struct Creator {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[setters(skip)]
pub command: Command,
#[serde(rename = "localtime")]
#[setters(skip)]
pub local_time: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<Status>,
#[serde(skip_serializing_if = "Option::is_none", rename = "autodelete")]
pub auto_delete: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recycle: Option<bool>,
}
impl Creator {
pub fn new(command: Command, local_time: String) -> Self {
Self {
name: None,
description: None,
command,
local_time,
status: None,
auto_delete: None,
recycle: None,
}
}
}
impl resource::Creator for Creator {
fn url_suffix() -> String {
"schedules".to_owned()
}
}
#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Setters)]
#[setters(strip_option, prefix = "with_")]
pub struct Modifier {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub command: Option<Command>,
#[serde(skip_serializing_if = "Option::is_none", rename = "localtime")]
pub local_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<Status>,
#[serde(skip_serializing_if = "Option::is_none", rename = "autodelete")]
pub auto_delete: Option<bool>,
}
impl Modifier {
pub fn new() -> Self {
Self::default()
}
}
impl resource::Modifier for Modifier {
type Id = String;
fn url_suffix(id: Self::Id) -> String {
format!("schedules/{}", id)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn serialize_command() {
let command = Command {
address: "/api/user/lights/1/state".into(),
request_method: CommandRequestMethod::Put,
body: json!({"on": true}),
};
let command_json = serde_json::to_value(command).unwrap();
let expected_json = json!({
"address": "/api/user/lights/1/state",
"method": "PUT",
"body": {
"on": true
}
});
assert_eq!(command_json, expected_json);
let creator = resource::group::Creator::new("test".into(), vec!["1".into()]);
let command = Command::from_creator(&creator, "user").unwrap();
let command_json = serde_json::to_value(command).unwrap();
let expected_json = json!({
"address": "/api/user/groups",
"method": "POST",
"body": {
"name": "test",
"lights": ["1"]
}
});
assert_eq!(command_json, expected_json);
let modifier = resource::light::StateModifier::new().with_on(true);
let command = Command::from_modifier(&modifier, "1".into(), "user").unwrap();
let command_json = serde_json::to_value(command).unwrap();
let expected_json = json!({
"address": "/api/user/lights/1/state",
"method": "PUT",
"body": {
"on": true
}
});
assert_eq!(command_json, expected_json);
let scanner = resource::light::Scanner::new();
let command = Command::from_scanner(&scanner, "user").unwrap();
let command_json = serde_json::to_value(command).unwrap();
let expected_json = json!({
"address": "/api/user/lights",
"method": "POST",
"body": {}
});
assert_eq!(command_json, expected_json);
}
#[test]
fn serialize_creator() {
let command = Command {
address: "/api/user/lights/1/state".into(),
request_method: CommandRequestMethod::Put,
body: json!({"on": true}),
};
let creator = Creator::new(command.clone(), "2020-01-01T00:00:00".into());
let creator_json = serde_json::to_value(creator).unwrap();
let expected_json = json!({
"command": {
"address": "/api/user/lights/1/state",
"method": "PUT",
"body": {
"on": true
}
},
"localtime": "2020-01-01T00:00:00"
});
assert_eq!(creator_json, expected_json);
let creator = Creator {
name: Some("test".into()),
description: Some("description test".into()),
command,
local_time: "2020-01-01T00:00:00".into(),
status: Some(Status::Enabled),
auto_delete: Some(false),
recycle: Some(true),
};
let creator_json = serde_json::to_value(creator).unwrap();
let expected_json = json!({
"name": "test",
"description": "description test",
"command": {
"address": "/api/user/lights/1/state",
"method": "PUT",
"body": {
"on": true
}
},
"localtime": "2020-01-01T00:00:00",
"status": "enabled",
"autodelete": false,
"recycle": true
});
assert_eq!(creator_json, expected_json);
}
#[test]
fn serialize_modifier() {
let modifier = Modifier::new();
let modifier_json = serde_json::to_value(modifier).unwrap();
let expected_json = json!({});
assert_eq!(modifier_json, expected_json);
let modifier = Modifier {
name: Some("test".into()),
description: Some("description test".into()),
command: Some(Command {
address: "/api/user/lights/1/state".into(),
request_method: CommandRequestMethod::Put,
body: json!({"on": true}),
}),
local_time: Some("2020-01-01T00:00:00".into()),
status: Some(Status::Disabled),
auto_delete: Some(true),
};
let modifier_json = serde_json::to_value(modifier).unwrap();
let expected_json = json!({
"name": "test",
"description": "description test",
"command": {
"address": "/api/user/lights/1/state",
"method": "PUT",
"body": {
"on": true
}
},
"localtime": "2020-01-01T00:00:00",
"status": "disabled",
"autodelete": true
});
assert_eq!(modifier_json, expected_json);
}
}