1use crate::message::MessageSerializer;
16
17#[derive(Clone, Debug, Default, PartialEq)]
68#[non_exhaustive]
69pub struct Any(serde_json::Map<String, serde_json::Value>);
70
71#[derive(thiserror::Error, Debug)]
96#[non_exhaustive]
97pub enum AnyError {
98 #[error("cannot serialize object into an Any, source={0}")]
100 Serialization(#[source] BoxedError),
101
102 #[error("cannot deserialize from an Any, source={0}")]
104 Deserialization(#[source] BoxedError),
105
106 #[error(
108 "mismatched typenames extracting from Any, the any has {has}, the target type is {want}"
109 )]
110 TypeMismatch {
111 has: String,
113 want: String,
115 },
116}
117
118impl AnyError {
119 pub(crate) fn ser<T: Into<BoxedError>>(v: T) -> Self {
120 Self::Serialization(v.into())
121 }
122
123 pub(crate) fn deser<T: Into<BoxedError>>(v: T) -> Self {
124 Self::Deserialization(v.into())
125 }
126
127 pub(crate) fn mismatch(has: &str, want: &str) -> Self {
128 Self::TypeMismatch {
129 has: has.into(),
130 want: want.into(),
131 }
132 }
133}
134
135type BoxedError = Box<dyn std::error::Error + Send + Sync>;
136type Error = AnyError;
137
138impl Any {
139 pub fn type_url(&self) -> Option<&str> {
158 self.0.get("@type").and_then(serde_json::Value::as_str)
159 }
160
161 pub fn from_msg<T>(message: &T) -> Result<Self, Error>
171 where
172 T: crate::message::Message,
173 {
174 let serializer = T::serializer();
175 let value = serializer.serialize_to_map(message)?;
176 Ok(Any(value))
177 }
178
179 pub fn to_msg<T>(&self) -> Result<T, Error>
190 where
191 T: crate::message::Message,
192 {
193 let map = &self.0;
194 let r#type = map
195 .get("@type")
196 .and_then(|v| v.as_str())
197 .ok_or_else(|| "@type field is missing or is not a string".to_string())
198 .map_err(Error::deser)?;
199 Self::check_typename(r#type, T::typename())?;
200
201 let serializer = T::serializer();
202 serializer.deserialize_from_map(map)
203 }
204
205 fn check_typename(has: &str, want: &str) -> Result<(), Error> {
206 if has == want {
207 return Ok(());
208 }
209 Err(Error::mismatch(has, want))
210 }
211}
212
213impl crate::message::Message for Any {
214 fn typename() -> &'static str {
215 "type.googleapis.com/google.protobuf.Any"
216 }
217
218 #[allow(private_interfaces)]
219 fn serializer() -> impl crate::message::MessageSerializer<Self> {
220 crate::message::ValueSerializer::<Self>::new()
221 }
222}
223
224#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
226impl serde::ser::Serialize for Any {
227 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
228 where
229 S: serde::ser::Serializer,
230 {
231 self.0.serialize(serializer)
232 }
233}
234
235use serde::de::Unexpected;
236type ValueMap = serde_json::Map<String, serde_json::Value>;
237
238const EXPECTED: &str = "a valid type URL string in the @type field";
239
240#[cfg_attr(not(feature = "_internal-semver"), doc(hidden))]
242impl<'de> serde::de::Deserialize<'de> for Any {
243 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
244 where
245 D: serde::Deserializer<'de>,
246 {
247 use serde::de::Error as _;
248 use serde_json::Value;
249 let value = ValueMap::deserialize(deserializer)?;
250 match value.get("@type") {
251 None => Ok(Any(value)),
252 Some(Value::String(s)) if validate_type_url(s) => Ok(Any(value)),
253 Some(Value::String(s)) => Err(D::Error::invalid_value(Unexpected::Str(s), &EXPECTED)),
254 Some(Value::Null) => Err(type_field_invalid_type("JSON null")),
255 Some(Value::Object(_)) => Err(type_field_invalid_type("JSON object")),
256 Some(Value::Array(_)) => Err(type_field_invalid_type("JSON array")),
257 Some(Value::Number(_)) => Err(type_field_invalid_type("JSON number")),
258 Some(Value::Bool(_)) => Err(type_field_invalid_type("JSON boolean")),
259 }
260 }
261}
262
263fn type_field_invalid_type<E>(reason: &str) -> E
264where
265 E: serde::de::Error,
266{
267 E::invalid_type(Unexpected::Other(reason), &EXPECTED)
268}
269
270fn validate_type_url(type_url: &str) -> bool {
271 match type_url.split_once("/") {
272 None => false,
273 Some((host, path)) => is_host(host) && is_protobuf_id(path),
274 }
275}
276
277fn is_host(host: &str) -> bool {
278 if host == "type.googleapis.com" {
279 return true;
280 }
281 if host.contains("_") {
282 return false;
283 }
284 url::Url::parse(format!("https://{host}").as_str()).is_ok()
286}
287
288fn is_protobuf_id(path: &str) -> bool {
289 path.split(".").all(is_identifier)
290}
291
292fn is_identifier(id: &str) -> bool {
293 !id.is_empty()
294 && id.chars().all(|c: char| c.is_alphanumeric() || c == '_')
295 && !id.starts_with(|c: char| c.is_ascii_digit())
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301 use crate::duration::*;
302 use crate::empty::Empty;
303 use crate::field_mask::*;
304 use crate::timestamp::*;
305 use serde_json::{Value, json};
306 use test_case::test_case;
307
308 type Result = anyhow::Result<()>;
309
310 #[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)]
311 #[serde(rename_all = "camelCase")]
312 #[non_exhaustive]
313 pub struct Stored {
314 #[serde(skip_serializing_if = "String::is_empty")]
315 pub parent: String,
316 #[serde(skip_serializing_if = "String::is_empty")]
317 pub id: String,
318 }
319
320 impl crate::message::Message for Stored {
321 fn typename() -> &'static str {
322 "type.googleapis.com/wkt.test.Stored"
323 }
324 }
325
326 #[test]
327 fn serialize_any() -> Result {
328 let d = Duration::clamp(60, 0);
329 let any = Any::from_msg(&d)?;
330 let any = Any::from_msg(&any)?;
331 assert_eq!(
332 any.type_url(),
333 Some("type.googleapis.com/google.protobuf.Any")
334 );
335 let got = serde_json::to_value(any)?;
336 let want = json!({
337 "@type": "type.googleapis.com/google.protobuf.Any",
338 "value": {
339 "@type": "type.googleapis.com/google.protobuf.Duration",
340 "value": "60s"
341 }
342 });
343 assert_eq!(got, want);
344 Ok(())
345 }
346
347 #[test]
348 fn deserialize_any() -> Result {
349 let input = json!({
350 "@type": "type.googleapis.com/google.protobuf.Any",
351 "value": {
352 "@type": "type.googleapis.com/google.protobuf.Duration",
353 "value": "60s"
354 }
355 });
356 let any = Any(input.as_object().unwrap().clone());
357 assert_eq!(
358 any.type_url(),
359 Some("type.googleapis.com/google.protobuf.Any")
360 );
361 let any = any.to_msg::<Any>()?;
362 assert_eq!(
363 any.type_url(),
364 Some("type.googleapis.com/google.protobuf.Duration")
365 );
366 let d = any.to_msg::<Duration>()?;
367 assert_eq!(d, Duration::clamp(60, 0));
368 Ok(())
369 }
370
371 #[test_case(json!({"value": "7"}))]
372 #[test_case(json!({"@type": "type.googleapis.com/foo"}))]
373 #[test_case(json!({"@type": "type.googleapis.com/foo_bar"}))]
374 #[test_case(json!({"@type": "type.googleapis.com/foo_bar.baz"}))]
375 #[test_case(json!({"@type": "type.googleapis.com/foo_bar.baz.Message"}))]
376 #[test_case(json!({"@type": "type.googleapis.com/foo_bar.baz.Message3"}))]
377 #[test_case(json!({"@type": "type.googleapis.com/foo2_bar.baz.Message3"}))]
378 fn deserialize_any_success(input: Value) {
379 let any = serde_json::from_value::<Any>(input.clone());
380 assert!(any.is_ok(), "{any:?} from {input:?}");
381 }
382
383 #[test_case(json!({"@type": "", "value": "7"}))]
384 #[test_case(json!({"@type": "type.googleapis.com/", "value": "7"}))]
385 #[test_case(json!({"@type": "/google.protobuf.Duration", "value": "7"}))]
386 #[test_case(json!({"@type": "type.googleapis.com/google.protobuf.7abc", "value": "7"}))]
387 #[test_case(json!({"@type": "type.googlea_pis.com/google.protobuf.Duration", "value": "7"}))]
388 #[test_case(json!({"@type": "abc_123/google.protobuf.Foo", "value": "7"}))]
389 #[test_case(json!({"@type": [], "value": "7"}); "type is array")]
390 #[test_case(json!({"@type": 7, "value": "7"}))]
391 #[test_case(json!({"@type": true, "value": "7"}))]
392 #[test_case(json!({"@type": null, "value": "7"}))]
393 #[test_case(json!({"@type": {}, "value": "7"}); "type is object")]
394 fn deserialize_bad_types(input: Value) {
395 let err = serde_json::from_value::<Any>(input).expect_err("should fail");
396 assert!(err.is_data(), "{err:?}");
397 }
398
399 #[test]
400 fn serialize_duration() -> Result {
401 let d = Duration::clamp(60, 0);
402 let any = Any::from_msg(&d)?;
403 assert_eq!(
404 any.type_url(),
405 Some("type.googleapis.com/google.protobuf.Duration")
406 );
407 let got = serde_json::to_value(any)?;
408 let want = json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": "60s"});
409 assert_eq!(got, want);
410 Ok(())
411 }
412
413 #[test]
414 fn deserialize_duration() -> Result {
415 let input =
416 json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": "60s"});
417 let any = Any(input.as_object().unwrap().clone());
418 assert_eq!(
419 any.type_url(),
420 Some("type.googleapis.com/google.protobuf.Duration")
421 );
422 let d = any.to_msg::<Duration>()?;
423 assert_eq!(d, Duration::clamp(60, 0));
424 Ok(())
425 }
426
427 #[test]
428 fn serialize_empty() -> Result {
429 let empty = Empty::default();
430 let any = Any::from_msg(&empty)?;
431 assert_eq!(
432 any.type_url(),
433 Some("type.googleapis.com/google.protobuf.Empty")
434 );
435 let got = serde_json::to_value(any)?;
436 let want = json!({"@type": "type.googleapis.com/google.protobuf.Empty"});
437 assert_eq!(got, want);
438 Ok(())
439 }
440
441 #[test]
442 fn deserialize_empty() -> Result {
443 let input = json!({"@type": "type.googleapis.com/google.protobuf.Empty"});
444 let any = Any(input.as_object().unwrap().clone());
445 assert_eq!(
446 any.type_url(),
447 Some("type.googleapis.com/google.protobuf.Empty")
448 );
449 let empty = any.to_msg::<Empty>()?;
450 assert_eq!(empty, Empty::default());
451 Ok(())
452 }
453
454 #[test]
455 fn serialize_field_mask() -> Result {
456 let d = FieldMask::default().set_paths(["a", "b"].map(str::to_string).to_vec());
457 let any = Any::from_msg(&d)?;
458 assert_eq!(
459 any.type_url(),
460 Some("type.googleapis.com/google.protobuf.FieldMask")
461 );
462 let got = serde_json::to_value(any)?;
463 let want =
464 json!({"@type": "type.googleapis.com/google.protobuf.FieldMask", "value": "a,b"});
465 assert_eq!(got, want);
466 Ok(())
467 }
468
469 #[test]
470 fn deserialize_field_mask() -> Result {
471 let input =
472 json!({"@type": "type.googleapis.com/google.protobuf.FieldMask", "value": "a,b"});
473 let any = Any(input.as_object().unwrap().clone());
474 assert_eq!(
475 any.type_url(),
476 Some("type.googleapis.com/google.protobuf.FieldMask")
477 );
478 let d = any.to_msg::<FieldMask>()?;
479 assert_eq!(
480 d,
481 FieldMask::default().set_paths(["a", "b"].map(str::to_string).to_vec())
482 );
483 Ok(())
484 }
485
486 #[test]
487 fn serialize_timestamp() -> Result {
488 let d = Timestamp::clamp(123, 0);
489 let any = Any::from_msg(&d)?;
490 assert_eq!(
491 any.type_url(),
492 Some("type.googleapis.com/google.protobuf.Timestamp")
493 );
494 let got = serde_json::to_value(any)?;
495 let want = json!({"@type": "type.googleapis.com/google.protobuf.Timestamp", "value": "1970-01-01T00:02:03Z"});
496 assert_eq!(got, want);
497 Ok(())
498 }
499
500 #[test]
501 fn deserialize_timestamp() -> Result {
502 let input = json!({"@type": "type.googleapis.com/google.protobuf.Timestamp", "value": "1970-01-01T00:02:03Z"});
503 let any = Any(input.as_object().unwrap().clone());
504 assert_eq!(
505 any.type_url(),
506 Some("type.googleapis.com/google.protobuf.Timestamp")
507 );
508 let d = any.to_msg::<Timestamp>()?;
509 assert_eq!(d, Timestamp::clamp(123, 0));
510 Ok(())
511 }
512
513 #[test]
514 fn serialize_generic() -> Result {
515 let d = Stored {
516 parent: "parent".to_string(),
517 id: "id".to_string(),
518 };
519 let any = Any::from_msg(&d)?;
520 assert_eq!(any.type_url(), Some("type.googleapis.com/wkt.test.Stored"));
521 let got = serde_json::to_value(any)?;
522 let want =
523 json!({"@type": "type.googleapis.com/wkt.test.Stored", "parent": "parent", "id": "id"});
524 assert_eq!(got, want);
525 Ok(())
526 }
527
528 #[test]
529 fn deserialize_generic() -> Result {
530 let input =
531 json!({"@type": "type.googleapis.com/wkt.test.Stored", "parent": "parent", "id": "id"});
532 let any = Any(input.as_object().unwrap().clone());
533 assert_eq!(any.type_url(), Some("type.googleapis.com/wkt.test.Stored"));
534 let d = any.to_msg::<Stored>()?;
535 assert_eq!(
536 d,
537 Stored {
538 parent: "parent".to_string(),
539 id: "id".to_string()
540 }
541 );
542 Ok(())
543 }
544
545 #[derive(Default, serde::Serialize, serde::Deserialize)]
546 struct DetectBadMessages(serde_json::Value);
547 impl crate::message::Message for DetectBadMessages {
548 fn typename() -> &'static str {
549 "not used"
550 }
551 }
552
553 #[test]
554 fn try_from_error() -> Result {
555 let input = DetectBadMessages(json!([2, 3]));
556 let got = Any::from_msg(&input);
557 assert!(got.is_err(), "{got:?}");
558
559 Ok(())
560 }
561
562 #[test]
563 fn deserialize_missing_value_field() -> Result {
564 let input = json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value-is-missing": "1.2s"});
565 let any = serde_json::from_value::<Any>(input)?;
566 let got = any.to_msg::<Duration>();
567 assert!(got.is_err());
568 Ok(())
569 }
570
571 #[test]
572 fn deserialize_invalid_value_field() -> Result {
573 let input =
574 json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": ["1.2s"]});
575 let any = serde_json::from_value::<Any>(input)?;
576 let got = any.to_msg::<Duration>();
577 assert!(got.is_err());
578 Ok(())
579 }
580
581 #[test]
582 fn deserialize_type_mismatch() -> Result {
583 let input =
584 json!({"@type": "type.googleapis.com/google.protobuf.Duration", "value": "1.2s"});
585 let any = serde_json::from_value::<Any>(input)?;
586 let got = any.to_msg::<Timestamp>();
587 assert!(got.is_err());
588 let error = got.err().unwrap();
589 assert!(
590 format!("{error}").contains("type.googleapis.com/google.protobuf.Duration"),
591 "{error}"
592 );
593 assert!(
594 format!("{error}").contains("type.googleapis.com/google.protobuf.Timestamp"),
595 "{error}"
596 );
597 Ok(())
598 }
599}