use crate::key::KeyInfo;
use crate::method::MethodInfo;
use arora_types::value::Value;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Incoming {
WriteValues {
values: HashMap<String, Value>,
},
ReadValues {
keys: Vec<String>,
},
ListKeys {
#[serde(default)]
path: Option<String>,
},
ListMethods {
#[serde(default)]
path: Option<String>,
},
Invoke {
method: String,
#[serde(default)]
args: HashMap<String, Value>,
#[serde(default)]
request_id: Option<String>,
},
}
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Outgoing {
WriteValuesResp {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
message: Option<String>,
},
ReadValuesResp {
values: HashMap<String, Value>,
},
ListKeysResp {
keys: Vec<KeyInfo>,
},
ListMethodsResp {
methods: Vec<MethodInfo>,
},
InvokeResp {
success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
request_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
value: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
message: Option<String>,
},
Error {
#[serde(skip_serializing_if = "Option::is_none")]
request_id: Option<String>,
message: String,
},
ValuesChanged {
values: HashMap<String, Value>,
},
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_incoming_write_values_deserialize() {
let json = r#"{"type": "write_values", "values": {"test/path": {"f64": 0.5}}}"#;
let msg: Incoming = serde_json::from_str(json).unwrap();
match msg {
Incoming::WriteValues { values } => {
assert!(values.contains_key("test/path"));
}
_ => panic!("Expected WriteValues message"),
}
}
#[test]
fn test_incoming_read_values_deserialize() {
let json = r#"{"type": "read_values", "keys": ["face/mouth", "face/eyes"]}"#;
let msg: Incoming = serde_json::from_str(json).unwrap();
match msg {
Incoming::ReadValues { keys } => {
assert_eq!(keys, vec!["face/mouth", "face/eyes"]);
}
_ => panic!("Expected ReadValues message"),
}
}
#[test]
fn test_incoming_list_keys_deserialize() {
let json = r#"{"type": "list_keys", "path": "face"}"#;
let msg: Incoming = serde_json::from_str(json).unwrap();
match msg {
Incoming::ListKeys { path } => {
assert_eq!(path, Some("face".to_string()));
}
_ => panic!("Expected ListKeys message"),
}
}
#[test]
fn test_incoming_invoke_deserialize() {
let json = r#"{"type": "invoke", "method": "reset", "request_id": "req-1"}"#;
let msg: Incoming = serde_json::from_str(json).unwrap();
match msg {
Incoming::Invoke {
method,
args,
request_id,
} => {
assert_eq!(method, "reset");
assert!(args.is_empty());
assert_eq!(request_id, Some("req-1".to_string()));
}
_ => panic!("Expected Invoke message"),
}
}
#[test]
fn test_outgoing_write_values_resp_serialize() {
let msg = Outgoing::WriteValuesResp {
success: true,
message: None,
};
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains(r#""type":"write_values_resp""#));
assert!(json.contains(r#""success":true"#));
assert!(!json.contains("message"));
}
#[test]
fn test_outgoing_invoke_resp_serialize() {
let msg = Outgoing::InvokeResp {
success: false,
request_id: Some("req-1".to_string()),
value: None,
message: Some("Method not found".to_string()),
};
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains(r#""type":"invoke_resp""#));
assert!(json.contains(r#""request_id":"req-1""#));
assert!(json.contains(r#""message":"Method not found""#));
}
}