1use std::fmt;
2
3use serde::Serialize;
4use serde_json::Value;
5
6use sofie_core::{Error, Result, Show, Shown};
7
8pub enum Reply {
9 Empty,
10 Json(Value),
11 Data(Box<dyn ReplyData>),
12 List(Vec<Reply>),
13}
14
15pub trait ReplyData: Send + Sync {
16 fn write_json(&self, out: &mut Vec<u8>) -> serde_json::Result<()>;
17
18 fn to_json(&self) -> serde_json::Result<Value>;
19
20 fn type_name(&self) -> &'static str {
21 std::any::type_name::<Self>()
22 }
23
24 fn shown(&self) -> Option<Shown<'_>> {
26 None
27 }
28}
29
30impl<T: Serialize + Send + Sync> ReplyData for T {
31 fn write_json(&self, out: &mut Vec<u8>) -> serde_json::Result<()> {
32 serde_json::to_writer(out, self)
33 }
34
35 fn to_json(&self) -> serde_json::Result<Value> {
36 serde_json::to_value(self)
37 }
38}
39
40pub struct Showing<T>(pub T);
42
43impl<T: Show + Serialize + Send + Sync> ReplyData for Showing<T> {
44 fn write_json(&self, out: &mut Vec<u8>) -> serde_json::Result<()> {
45 self.0.write_json(out)
46 }
47
48 fn to_json(&self) -> serde_json::Result<Value> {
49 self.0.to_json()
50 }
51
52 fn shown(&self) -> Option<Shown<'_>> {
53 Some(self.0.show())
54 }
55}
56
57impl Show for Reply {
58 fn show(&self) -> Shown<'_> {
59 self.shown().unwrap_or(Shown::Null)
60 }
61}
62
63impl Reply {
64 #[must_use]
65 pub fn data(data: impl Serialize + Send + Sync + 'static) -> Self {
66 Self::Data(Box::new(data))
67 }
68
69 #[must_use]
71 pub fn shown(&self) -> Option<Shown<'_>> {
72 match self {
73 Self::Empty => Some(Shown::Null),
74 Self::Json(value) => Some(value.show()),
75 Self::Data(data) => data.shown(),
76 Self::List(replies) => replies
77 .iter()
78 .all(|reply| reply.shown().is_some())
79 .then_some(Shown::List(replies)),
80 }
81 }
82
83 pub fn into_json(self) -> Result<Value> {
84 match self {
85 Self::Json(value) => Ok(value),
86 other => other.to_json(),
87 }
88 }
89
90 pub fn to_json(&self) -> Result<Value> {
91 match self {
92 Self::Empty => Ok(Value::Null),
93 Self::Json(value) => Ok(value.clone()),
94 Self::Data(data) => data
95 .to_json()
96 .map_err(|error| refusal(data.as_ref(), error)),
97 Self::List(replies) => replies.iter().map(Self::to_json).collect(),
98 }
99 }
100
101 pub fn write_json(&self, out: &mut Vec<u8>) -> Result<()> {
102 match self {
103 Self::Empty => out.extend_from_slice(b"null"),
104 Self::Json(value) => serde_json::to_writer(out, value)
105 .map_err(|error| Error::internal("serializing a reply", error))?,
106 Self::Data(data) => data
107 .write_json(out)
108 .map_err(|error| refusal(data.as_ref(), error))?,
109 Self::List(replies) => {
110 out.push(b'[');
111 for (index, reply) in replies.iter().enumerate() {
112 if index > 0 {
113 out.push(b',');
114 }
115 reply.write_json(out)?;
116 }
117 out.push(b']');
118 }
119 }
120 Ok(())
121 }
122}
123
124fn refusal(data: &dyn ReplyData, error: serde_json::Error) -> Error {
125 Error::internal(format!("serializing a `{}` reply", data.type_name()), error)
126}
127
128impl fmt::Debug for Reply {
129 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
130 match self {
131 Self::Empty => formatter.write_str("Empty"),
132 Self::Json(value) => formatter.debug_tuple("Json").field(value).finish(),
133 Self::Data(data) => formatter
134 .debug_tuple("Data")
135 .field(&data.type_name())
136 .finish(),
137 Self::List(replies) => formatter.debug_tuple("List").field(replies).finish(),
138 }
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use std::collections::BTreeMap;
145
146 use serde_json::json;
147
148 use super::*;
149
150 fn written(reply: &Reply) -> String {
151 let mut out = Vec::new();
152 reply.write_json(&mut out).unwrap();
153 String::from_utf8(out).unwrap()
154 }
155
156 #[test]
157 fn typed_data_is_written_as_the_value_it_converts_to() {
158 let reply = Reply::List(vec![
159 Reply::data(BTreeMap::from([("name", "a\"b"), ("total", "1")])),
160 Reply::Empty,
161 Reply::Json(json!({ "id": 2 })),
162 ]);
163
164 let text = written(&reply);
165 assert_eq!(text, r#"[{"name":"a\"b","total":"1"},null,{"id":2}]"#);
166 assert_eq!(
167 serde_json::from_str::<Value>(&text).unwrap(),
168 reply.into_json().unwrap()
169 );
170 }
171
172 #[test]
173 fn data_json_cannot_hold_is_an_internal_error_naming_its_type() {
174 let reply = Reply::data(BTreeMap::from([((1, 2), "pair")]));
175
176 let error = reply.write_json(&mut Vec::new()).unwrap_err();
177 assert!(matches!(error, Error::Internal { .. }));
178 assert!(error.to_string().contains("BTreeMap"), "{error}");
179 assert!(matches!(reply.to_json(), Err(Error::Internal { .. })));
180 }
181}