1use base64::{Engine, engine::general_purpose::STANDARD};
16
17pub type DoubleValue = f64;
27
28pub type FloatValue = f32;
38
39pub type Int64Value = i64;
49
50pub type UInt64Value = u64;
60
61pub type Int32Value = i32;
71
72pub type UInt32Value = u32;
82
83pub type BoolValue = bool;
93
94pub type StringValue = String;
104
105pub type BytesValue = bytes::Bytes;
115
116macro_rules! impl_message {
117 ($t: ty) => {
118 impl crate::message::Message for $t {
119 fn typename() -> &'static str {
120 concat!("type.googleapis.com/google.protobuf.", stringify!($t))
121 }
122 fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
123 where
124 Self: serde::ser::Serialize + Sized,
125 {
126 let map: crate::message::Map = [
127 (
128 "@type",
129 serde_json::Value::String(Self::typename().to_string()),
130 ),
131 ("value", serde_json::json!(self)),
132 ]
133 .into_iter()
134 .map(|(k, v)| (k.to_string(), v))
135 .collect();
136 Ok(map)
137 }
138 fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
139 where
140 Self: serde::de::DeserializeOwned,
141 {
142 crate::message::from_value::<Self>(map)
143 }
144 }
145 };
146}
147
148impl_message!(DoubleValue);
149impl_message!(FloatValue);
150impl_message!(Int32Value);
151impl_message!(UInt32Value);
152impl_message!(BoolValue);
153impl_message!(StringValue);
154
155fn encode_string<T>(value: String) -> Result<crate::message::Map, crate::AnyError>
156where
157 T: crate::message::Message,
158{
159 let map: crate::message::Map = [
160 (
161 "@type",
162 serde_json::Value::String(T::typename().to_string()),
163 ),
164 ("value", serde_json::Value::String(value)),
165 ]
166 .into_iter()
167 .map(|(k, v)| (k.to_string(), v))
168 .collect();
169 Ok(map)
170}
171
172impl crate::message::Message for UInt64Value {
173 fn typename() -> &'static str {
174 "type.googleapis.com/google.protobuf.UInt64Value"
175 }
176 fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
177 where
178 Self: serde::ser::Serialize + Sized,
179 {
180 encode_string::<Self>(self.to_string())
181 }
182 fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
183 where
184 Self: serde::de::DeserializeOwned,
185 {
186 map.get("value")
187 .ok_or_else(crate::message::missing_value_field)?
188 .as_str()
189 .ok_or_else(expected_string_value)?
190 .parse::<UInt64Value>()
191 .map_err(crate::AnyError::deser)
192 }
193}
194
195impl crate::message::Message for Int64Value {
196 fn typename() -> &'static str {
197 "type.googleapis.com/google.protobuf.Int64Value"
198 }
199 fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
200 where
201 Self: serde::ser::Serialize + Sized,
202 {
203 encode_string::<Self>(self.to_string())
204 }
205 fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
206 where
207 Self: serde::de::DeserializeOwned,
208 {
209 map.get("value")
210 .ok_or_else(crate::message::missing_value_field)?
211 .as_str()
212 .ok_or_else(expected_string_value)?
213 .parse::<Int64Value>()
214 .map_err(crate::AnyError::deser)
215 }
216}
217
218impl crate::message::Message for BytesValue {
219 fn typename() -> &'static str {
220 "type.googleapis.com/google.protobuf.BytesValue"
221 }
222 fn to_map(&self) -> Result<crate::message::Map, crate::AnyError>
223 where
224 Self: serde::ser::Serialize + Sized,
225 {
226 encode_string::<Self>(STANDARD.encode(self))
227 }
228 fn from_map(map: &crate::message::Map) -> Result<Self, crate::AnyError>
229 where
230 Self: serde::de::DeserializeOwned,
231 {
232 let s = map
233 .get("value")
234 .ok_or_else(crate::message::missing_value_field)?
235 .as_str()
236 .ok_or_else(expected_string_value)?;
237 STANDARD
238 .decode(s)
239 .map(BytesValue::from)
240 .map_err(crate::AnyError::deser)
241 }
242}
243
244fn expected_string_value() -> crate::AnyError {
245 crate::AnyError::deser("expected value field to be a string")
246}
247
248#[cfg(test)]
249mod test {
250 use super::*;
251 use crate::Any;
252 use crate::message::Message;
253 type Result = std::result::Result<(), Box<dyn std::error::Error>>;
254 use test_case::test_case;
255
256 const HELLO_WORLD_BASE64: &str = "SGVsbG8sIFdvcmxkIQ==";
258
259 #[test_case(1234.5 as DoubleValue, 1234.5, "DoubleValue")]
260 #[test_case(9876.5 as FloatValue, 9876.5, "FloatValue")]
261 #[test_case(-123 as Int64Value, "-123", "Int64Value")]
262 #[test_case(123 as UInt64Value, "123", "UInt64Value")]
263 #[test_case(-123 as Int32Value, -123, "Int32Value")]
264 #[test_case(123 as UInt32Value, 123, "UInt32Value")]
265 #[test_case(true as BoolValue, true, "BoolValue")]
266 #[test_case(StringValue::from("Hello, World!"), "Hello, World!", "StringValue")]
267 #[test_case(BytesValue::from("Hello, World!"), HELLO_WORLD_BASE64, "BytesValue")]
268 fn test_wrapper_in_any<I, V>(input: I, value: V, typename: &str) -> Result
269 where
270 I: crate::message::Message
271 + std::fmt::Debug
272 + PartialEq
273 + serde::de::DeserializeOwned
274 + serde::ser::Serialize,
275 V: serde::ser::Serialize,
276 {
277 let any = Any::try_from(&input)?;
278 let got = serde_json::to_value(&any)?;
279 let want = serde_json::json!({
280 "@type": format!("type.googleapis.com/google.protobuf.{}", typename),
281 "value": value,
282 });
283 assert_eq!(got, want);
284 let output = any.try_into_message::<I>()?;
285 assert_eq!(output, input);
286 Ok(())
287 }
288
289 #[test_case(Int32Value::default(), DoubleValue::default())]
290 #[test_case(Int32Value::default(), FloatValue::default())]
291 #[test_case(DoubleValue::default(), Int64Value::default())]
292 #[test_case(DoubleValue::default(), UInt64Value::default())]
293 #[test_case(DoubleValue::default(), Int32Value::default())]
294 #[test_case(DoubleValue::default(), UInt32Value::default())]
295 #[test_case(DoubleValue::default(), BoolValue::default())]
296 #[test_case(DoubleValue::default(), StringValue::default())]
297 #[test_case(DoubleValue::default(), BytesValue::default())]
298 fn test_wrapper_in_any_with_bad_typenames<T, U>(from: T, _into: U) -> Result
299 where
300 T: Message + std::fmt::Debug + serde::ser::Serialize,
301 U: Message + std::fmt::Debug + serde::de::DeserializeOwned,
302 {
303 let any = Any::try_from(&from)?;
304 assert!(any.try_into_message::<U>().is_err());
305 Ok(())
306 }
307
308 #[test_case(Int64Value::default(), "Int64Value")]
309 #[test_case(UInt64Value::default(), "UInt64Value")]
310 fn test_wrapper_bad_encoding<T>(_input: T, typename: &str) -> Result
311 where
312 T: Message + std::fmt::Debug + serde::ser::Serialize + serde::de::DeserializeOwned,
313 {
314 let map = serde_json::json!({
315 "@type": format!("type.googleapis.com/google.protobuf.{}", typename),
316 "value": 0,
317 });
318 let e = T::from_map(map.as_object().unwrap());
319 assert!(e.is_err());
320 let fmt = format!("{:?}", e);
321 assert!(fmt.contains("expected value field to be a string"), "{fmt}");
322 Ok(())
323 }
324
325 #[test]
326 fn test_wrapper_bad_encoding_base64() -> Result {
327 let map = serde_json::json!({
328 "@type": "type.googleapis.com/google.protobuf.BytesValue",
329 "value": "Oops, I forgot to base64 encode this.",
330 });
331 assert!(BytesValue::from_map(map.as_object().unwrap()).is_err());
332 Ok(())
333 }
334}