appletheia_application/outbox/command/
serialized_command.rs1use std::{fmt, fmt::Display, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4
5use super::SerializedCommandError;
6
7#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
8#[serde(transparent)]
9pub struct SerializedCommand(serde_json::Value);
10
11impl SerializedCommand {
12 pub fn new(value: serde_json::Value) -> Result<Self, SerializedCommandError> {
13 Self::validate(&value)?;
14 Ok(Self(value))
15 }
16
17 pub fn value(&self) -> &serde_json::Value {
18 &self.0
19 }
20
21 fn validate(value: &serde_json::Value) -> Result<(), SerializedCommandError> {
22 if value.is_null() {
23 return Err(SerializedCommandError::NullPayload);
24 }
25 Ok(())
26 }
27}
28
29impl TryFrom<serde_json::Value> for SerializedCommand {
30 type Error = SerializedCommandError;
31
32 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
33 Self::new(value)
34 }
35}
36
37impl Display for SerializedCommand {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 write!(f, "{}", self.0)
40 }
41}
42
43impl FromStr for SerializedCommand {
44 type Err = SerializedCommandError;
45
46 fn from_str(s: &str) -> Result<Self, Self::Err> {
47 let value = serde_json::from_str(s)?;
48 Self::new(value)
49 }
50}
51
52impl TryFrom<&str> for SerializedCommand {
53 type Error = SerializedCommandError;
54
55 fn try_from(value: &str) -> Result<Self, Self::Error> {
56 Self::from_str(value)
57 }
58}
59
60impl TryFrom<String> for SerializedCommand {
61 type Error = SerializedCommandError;
62
63 fn try_from(value: String) -> Result<Self, Self::Error> {
64 let json = serde_json::from_str::<serde_json::Value>(&value)?;
65 Self::new(json)
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
74 fn rejects_null() {
75 let err = SerializedCommand::try_from(serde_json::Value::Null).expect_err("null rejected");
76 assert!(matches!(err, SerializedCommandError::NullPayload));
77 }
78
79 #[test]
80 fn accepts_json_object() {
81 let value = serde_json::json!({ "name": "apple" });
82 let command = SerializedCommand::try_from(value.clone()).expect("valid");
83 assert_eq!(command.value(), &value);
84 }
85
86 #[test]
87 fn parses_from_str() {
88 let command: SerializedCommand = r#"{"name":"banana"}"#.parse().unwrap();
89 assert_eq!(command.value(), &serde_json::json!({ "name": "banana" }));
90 }
91
92 #[test]
93 fn detects_invalid_json() {
94 let err = SerializedCommand::try_from("not-json").expect_err("invalid json");
95 assert!(matches!(err, SerializedCommandError::Json { .. }));
96 }
97}