1use serde::{Deserialize, Serialize};
8
9#[non_exhaustive]
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(tag = "type", rename_all = "snake_case")]
16pub enum MessagePayload {
17 Text {
19 content: String,
21 },
22 Structured {
24 data: serde_json::Value,
26 },
27 Binary {
29 mime_type: String,
31 #[serde(with = "base64_bytes")]
33 data: Vec<u8>,
34 },
35}
36
37mod base64_bytes {
39 use serde::{Deserialize, Deserializer, Serializer};
40
41 pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
44 where
45 S: Serializer,
46 {
47 serializer.serialize_bytes(bytes)
48 }
49
50 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
51 where
52 D: Deserializer<'de>,
53 {
54 let bytes: Vec<u8> = Vec::deserialize(deserializer)?;
56 Ok(bytes)
57 }
58}
59
60impl MessagePayload {
61 pub fn text(content: impl Into<String>) -> Self {
63 Self::Text {
64 content: content.into(),
65 }
66 }
67
68 pub fn structured(data: serde_json::Value) -> Self {
70 Self::Structured { data }
71 }
72
73 pub fn binary(mime_type: impl Into<String>, data: Vec<u8>) -> Self {
75 Self::Binary {
76 mime_type: mime_type.into(),
77 data,
78 }
79 }
80
81 pub fn is_text(&self) -> bool {
83 matches!(self, Self::Text { .. })
84 }
85
86 pub fn is_binary(&self) -> bool {
88 matches!(self, Self::Binary { .. })
89 }
90
91 pub fn as_text(&self) -> Option<&str> {
93 match self {
94 Self::Text { content } => Some(content),
95 _ => None,
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn text_payload_creation() {
106 let payload = MessagePayload::text("hello");
107 assert!(payload.is_text());
108 assert!(!payload.is_binary());
109 assert_eq!(payload.as_text(), Some("hello"));
110 }
111
112 #[test]
113 fn structured_payload_creation() {
114 let data = serde_json::json!({"key": "value"});
115 let payload = MessagePayload::structured(data.clone());
116 assert!(!payload.is_text());
117 assert!(!payload.is_binary());
118 assert_eq!(payload.as_text(), None);
119 }
120
121 #[test]
122 fn binary_payload_creation() {
123 let payload = MessagePayload::binary("audio/wav", vec![0u8; 16]);
124 assert!(payload.is_binary());
125 assert!(!payload.is_text());
126 assert_eq!(payload.as_text(), None);
127 }
128
129 #[test]
130 fn text_payload_serde_roundtrip() {
131 let payload = MessagePayload::text("hello world");
132 let json = serde_json::to_string(&payload).unwrap();
133 let restored: MessagePayload = serde_json::from_str(&json).unwrap();
134 assert_eq!(restored.as_text(), Some("hello world"));
135 }
136
137 #[test]
138 fn structured_payload_serde_roundtrip() {
139 let data = serde_json::json!({"key": "value", "count": 42});
140 let payload = MessagePayload::structured(data.clone());
141 let json = serde_json::to_string(&payload).unwrap();
142 let restored: MessagePayload = serde_json::from_str(&json).unwrap();
143 match restored {
144 MessagePayload::Structured { data: d } => {
145 assert_eq!(d["key"], "value");
146 assert_eq!(d["count"], 42);
147 }
148 _ => panic!("expected Structured variant"),
149 }
150 }
151
152 #[test]
153 fn binary_payload_serde_roundtrip() {
154 let original_data = vec![0u8, 1, 2, 3, 255, 128];
155 let payload = MessagePayload::binary("audio/wav", original_data.clone());
156 let json = serde_json::to_string(&payload).unwrap();
157 let restored: MessagePayload = serde_json::from_str(&json).unwrap();
158 match restored {
159 MessagePayload::Binary { mime_type, data } => {
160 assert_eq!(mime_type, "audio/wav");
161 assert_eq!(data, original_data);
162 }
163 _ => panic!("expected Binary variant"),
164 }
165 }
166
167 #[test]
168 fn message_payload_is_send_sync() {
169 fn assert_send_sync<T: Send + Sync>() {}
170 assert_send_sync::<MessagePayload>();
171 }
172}