1use serde::de::{self, DeserializeOwned};
4use serde::ser::SerializeMap;
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use serde_json::Value;
7
8use crate::error::OpError;
9use crate::exact::{require_exact_keys, required};
10use crate::{DISCRIMINANT_CODE, ENVELOPE_VERSION};
11
12#[derive(Debug, Clone, PartialEq)]
21pub enum Envelope<T> {
22 Success {
24 version: String,
26 data: T,
28 },
29 Failure {
31 version: String,
33 error: OpError,
35 },
36}
37
38impl<T> Envelope<T> {
39 #[must_use]
41 pub fn success(data: T) -> Self {
42 Self::Success {
43 version: ENVELOPE_VERSION.to_owned(),
44 data,
45 }
46 }
47
48 #[must_use]
50 pub fn failure(error: OpError) -> Self {
51 Self::Failure {
52 version: ENVELOPE_VERSION.to_owned(),
53 error,
54 }
55 }
56
57 #[must_use]
59 pub fn version(&self) -> &str {
60 match self {
61 Self::Success { version, .. } | Self::Failure { version, .. } => version,
62 }
63 }
64
65 #[must_use]
67 pub fn is_ok(&self) -> bool {
68 matches!(self, Self::Success { .. })
69 }
70
71 #[must_use]
73 pub fn data(&self) -> Option<&T> {
74 match self {
75 Self::Success { data, .. } => Some(data),
76 Self::Failure { .. } => None,
77 }
78 }
79
80 #[must_use]
82 pub fn error(&self) -> Option<&OpError> {
83 match self {
84 Self::Failure { error, .. } => Some(error),
85 Self::Success { .. } => None,
86 }
87 }
88
89 pub fn into_result(self) -> Result<T, OpError> {
109 match self {
110 Self::Success { data, .. } => Ok(data),
111 Self::Failure { error, .. } => Err(error),
112 }
113 }
114}
115
116impl<T> From<Result<T, OpError>> for Envelope<T> {
117 fn from(result: Result<T, OpError>) -> Self {
118 match result {
119 Ok(data) => Self::success(data),
120 Err(error) => Self::failure(error),
121 }
122 }
123}
124
125impl<T> Serialize for Envelope<T>
126where
127 T: Serialize,
128{
129 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130 where
131 S: Serializer,
132 {
133 let mut map = serializer.serialize_map(Some(4))?;
134 match self {
135 Self::Success { version, data } => {
136 map.serialize_entry("version", version)?;
137 map.serialize_entry("ok", &true)?;
138 map.serialize_entry("data", data)?;
139 map.serialize_entry("error", &None::<OpError>)?;
140 }
141 Self::Failure { version, error } => {
142 map.serialize_entry("version", version)?;
143 map.serialize_entry("ok", &false)?;
144 map.serialize_entry("data", &None::<()>)?;
145 map.serialize_entry("error", error)?;
146 }
147 }
148 map.end()
149 }
150}
151
152impl<'de, T> Deserialize<'de> for Envelope<T>
153where
154 T: DeserializeOwned,
155{
156 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
157 where
158 D: Deserializer<'de>,
159 {
160 let value = Value::deserialize(deserializer)?;
161 envelope_from_value(value).map_err(de::Error::custom)
162 }
163}
164
165fn envelope_from_value<T>(value: Value) -> Result<Envelope<T>, String>
166where
167 T: DeserializeOwned,
168{
169 let map = match value {
170 Value::Object(map) => map,
171 _ => {
172 return Err(format!(
173 "{DISCRIMINANT_CODE}: envelope must be a JSON object"
174 ));
175 }
176 };
177 require_exact_keys(&map, &["version", "ok", "data", "error"])?;
178 let version = match required(&map, "version")? {
179 Value::String(version) => version.clone(),
180 _ => {
181 return Err(format!(
182 "{DISCRIMINANT_CODE}: version must be a JSON string"
183 ));
184 }
185 };
186 let ok = match required(&map, "ok")? {
187 Value::Bool(ok) => *ok,
188 _ => {
189 return Err(format!("{DISCRIMINANT_CODE}: ok must be a JSON boolean"));
190 }
191 };
192 let data = required(&map, "data")?;
193 let error = required(&map, "error")?;
194 if ok {
195 if !error.is_null() {
196 return Err(format!(
197 "{DISCRIMINANT_CODE}: ok is true and error is not null"
198 ));
199 }
200 let data = T::deserialize(data).map_err(|error| format!("{DISCRIMINANT_CODE}: {error}"))?;
201 Ok(Envelope::Success { version, data })
202 } else {
203 if !data.is_null() {
204 return Err(format!(
205 "{DISCRIMINANT_CODE}: ok is false and data is not null"
206 ));
207 }
208 if error.is_null() {
209 return Err(format!(
210 "{DISCRIMINANT_CODE}: ok is false and error is null"
211 ));
212 }
213 let error =
214 OpError::deserialize(error).map_err(|error| format!("{DISCRIMINANT_CODE}: {error}"))?;
215 Ok(Envelope::Failure { version, error })
216 }
217}