Skip to main content

appletheia_application/outbox/command/
serialized_command.rs

1use 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}