use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::error::ParseError;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RoutineResponse {
#[serde(flatten)]
fields: HashMap<String, serde_json::Value>,
}
impl RoutineResponse {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn contains_key(&self, key: &str) -> bool {
self.fields.contains_key(key)
}
#[must_use]
pub fn get(&self, key: &str) -> Option<&serde_json::Value> {
self.fields.get(key)
}
pub fn get_as<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T, ParseError> {
let value = self
.fields
.get(key)
.ok_or_else(|| ParseError::MissingField(key.to_string()))?;
serde_json::from_value(value.clone()).map_err(Into::into)
}
#[must_use]
pub fn try_get_as<T: serde::de::DeserializeOwned>(&self, key: &str) -> Option<T> {
self.get_as(key).ok()
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &serde_json::Value)> {
self.fields.iter()
}
#[must_use]
pub fn len(&self) -> usize {
self.fields.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
#[must_use]
pub fn raw(&self) -> &HashMap<String, serde_json::Value> {
&self.fields
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_simple_response() {
let json = r#"{"POWER":"ON"}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.len(), 1);
assert!(response.contains_key("POWER"));
assert!(!response.contains_key("Dimmer"));
}
#[test]
fn parse_multi_field_response() {
let json = r#"{"POWER1":"ON","POWER2":"OFF","Dimmer":75,"CT":350}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
assert_eq!(response.len(), 4);
assert!(response.contains_key("POWER1"));
assert!(response.contains_key("POWER2"));
assert!(response.contains_key("Dimmer"));
assert!(response.contains_key("CT"));
}
#[test]
fn get_typed_string() {
let json = r#"{"POWER":"ON"}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let power: String = response.get_as("POWER").unwrap();
assert_eq!(power, "ON");
}
#[test]
fn get_typed_number() {
let json = r#"{"Dimmer":75,"CT":350}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let dimmer: u8 = response.get_as("Dimmer").unwrap();
assert_eq!(dimmer, 75);
let ct: u16 = response.get_as("CT").unwrap();
assert_eq!(ct, 350);
}
#[test]
fn get_missing_field_error() {
let response = RoutineResponse::new();
let result: Result<String, _> = response.get_as("Missing");
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), ParseError::MissingField(_)));
}
#[test]
fn try_get_missing_returns_none() {
let response = RoutineResponse::new();
let result: Option<String> = response.try_get_as("Missing");
assert!(result.is_none());
}
#[test]
fn try_get_existing_returns_value() {
let json = r#"{"Dimmer":75}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let result: Option<u8> = response.try_get_as("Dimmer");
assert_eq!(result, Some(75));
}
#[test]
fn try_get_wrong_type_returns_none() {
let json = r#"{"POWER":"ON"}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let result: Option<u8> = response.try_get_as("POWER");
assert!(result.is_none());
}
#[test]
fn empty_response() {
let json = r#"{}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
assert!(response.is_empty());
assert_eq!(response.len(), 0);
}
#[test]
fn iterate_over_fields() {
let json = r#"{"POWER":"ON","Dimmer":75}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let keys: Vec<&String> = response.iter().map(|(k, _)| k).collect();
assert_eq!(keys.len(), 2);
assert!(keys.contains(&&"POWER".to_string()));
assert!(keys.contains(&&"Dimmer".to_string()));
}
#[test]
fn raw_access() {
let json = r#"{"POWER":"ON"}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let raw = response.raw();
assert!(raw.contains_key("POWER"));
}
#[test]
fn response_is_cloneable() {
let json = r#"{"POWER":"ON"}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let cloned = response.clone();
assert_eq!(response.len(), cloned.len());
}
#[test]
fn response_is_serializable() {
let json = r#"{"Dimmer":75,"POWER":"ON"}"#;
let response: RoutineResponse = serde_json::from_str(json).unwrap();
let serialized = serde_json::to_string(&response).unwrap();
let reparsed: RoutineResponse = serde_json::from_str(&serialized).unwrap();
assert_eq!(response.len(), reparsed.len());
}
}