1use std::any::TypeId;
17use std::collections::HashMap;
18use std::fmt;
19use std::io::Cursor;
20use std::marker::PhantomData;
21use std::sync::LazyLock;
22
23use enum_as_inner::EnumAsInner;
24use hyperactor_config::AttrValue;
25use serde::Deserialize;
26use serde::Serialize;
27use serde::de::DeserializeOwned;
28use serde::ser::SerializeStruct;
29pub use typeuri::Named;
30pub use typeuri::intern_typename;
31
32pub mod config;
33
34pub const BROKEN_TYPEHASH: u64 = 0;
36
37#[doc(hidden)]
38pub trait NamedDumpable: Named + Serialize + for<'de> Deserialize<'de> {
42 fn dump(data: Any) -> Result<serde_json::Value>;
44}
45
46impl<T: Named + Serialize + for<'de> Deserialize<'de>> NamedDumpable for T {
47 fn dump(data: Any) -> Result<serde_json::Value> {
48 let value = data.deserialized::<Self>()?;
49 Ok(serde_json::to_value(value)?)
50 }
51}
52
53#[doc(hidden)]
54#[derive(Debug)]
55pub struct TypeInfo {
56 pub typename: fn() -> &'static str,
58 pub typehash: fn() -> u64,
60 pub typeid: fn() -> TypeId,
62 pub port: fn() -> u64,
64 pub dump: Option<fn(Any) -> Result<serde_json::Value>>,
66 pub arm_unchecked: unsafe fn(*const ()) -> Option<&'static str>,
68 pub endpoint_name: unsafe fn(*const ()) -> Option<String>,
74}
75
76#[allow(dead_code)]
77impl TypeInfo {
78 pub fn get(typehash: u64) -> Option<&'static TypeInfo> {
80 TYPE_INFO.get(&typehash).map(|v| &**v)
81 }
82
83 pub fn get_by_typeid(typeid: TypeId) -> Option<&'static TypeInfo> {
85 TYPE_INFO_BY_TYPE_ID.get(&typeid).map(|v| &**v)
86 }
87
88 pub fn of<T: ?Sized + 'static>() -> Option<&'static TypeInfo> {
90 Self::get_by_typeid(TypeId::of::<T>())
91 }
92
93 pub fn typename(&self) -> &'static str {
95 (self.typename)()
96 }
97
98 pub fn typehash(&self) -> u64 {
100 (self.typehash)()
101 }
102
103 pub fn typeid(&self) -> TypeId {
105 (self.typeid)()
106 }
107
108 pub fn port(&self) -> u64 {
110 (self.port)()
111 }
112
113 pub fn dump(&self, data: Any) -> Result<serde_json::Value> {
115 if let Some(dump) = self.dump {
116 (dump)(data)
117 } else {
118 Err(Error::MissingDumper(self.typehash()))
119 }
120 }
121
122 pub unsafe fn arm_unchecked(&self, value: *const ()) -> Option<&'static str> {
127 unsafe { (self.arm_unchecked)(value) }
129 }
130
131 pub unsafe fn endpoint_name(&self, value: *const ()) -> Option<String> {
136 unsafe { (self.endpoint_name)(value) }
138 }
139}
140
141inventory::collect!(TypeInfo);
142
143static TYPE_INFO: LazyLock<HashMap<u64, &'static TypeInfo>> = LazyLock::new(|| {
145 inventory::iter::<TypeInfo>()
146 .map(|entry| (entry.typehash(), entry))
147 .collect()
148});
149
150static TYPE_INFO_BY_TYPE_ID: LazyLock<HashMap<std::any::TypeId, &'static TypeInfo>> =
152 LazyLock::new(|| {
153 TYPE_INFO
154 .values()
155 .map(|info| (info.typeid(), &**info))
156 .collect()
157 });
158
159#[macro_export]
165macro_rules! register_type {
166 ($type:ty) => {
167 $crate::submit! {
168 $crate::TypeInfo {
169 typename: <$type as $crate::Named>::typename,
170 typehash: <$type as $crate::Named>::typehash,
171 typeid: <$type as $crate::Named>::typeid,
172 port: <$type as $crate::Named>::port,
173 dump: Some(<$type as $crate::NamedDumpable>::dump),
174 arm_unchecked: <$type as $crate::Named>::arm_unchecked,
175 endpoint_name: |ptr| {
176 unsafe { <$type as $crate::Named>::arm_unchecked(ptr).map(|s| s.to_string()) }
178 },
179 }
180 }
181 };
182}
183
184#[doc(hidden)]
186pub use inventory::submit;
187
188#[derive(
190 Debug,
191 Clone,
192 Copy,
193 Serialize,
194 Deserialize,
195 PartialEq,
196 Eq,
197 AttrValue,
198 typeuri::Named,
199 strum::EnumIter,
200 strum::Display,
201 strum::EnumString
202)]
203pub enum Encoding {
204 #[strum(to_string = "bincode")]
206 Bincode,
207 #[strum(to_string = "serde_json")]
209 Json,
210 #[strum(to_string = "serde_multipart")]
212 Multipart,
213}
214
215pub mod encoding {
217 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
219 pub struct AnyEncoding;
220
221 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
223 pub struct Bincode;
224
225 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
227 pub struct Json;
228
229 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
231 pub struct Multipart;
232}
233
234mod private {
235 pub trait Sealed {}
236}
237
238pub trait EncodingMarker: private::Sealed + Clone + Copy + fmt::Debug + PartialEq + Eq {
240 #[doc(hidden)]
241 const EXPECTED_ENCODING: Option<Encoding>;
242}
243
244pub trait StaticEncoding: EncodingMarker {
246 #[doc(hidden)]
247 const ENCODING: Encoding;
248}
249
250impl private::Sealed for encoding::AnyEncoding {}
251
252impl EncodingMarker for encoding::AnyEncoding {
253 const EXPECTED_ENCODING: Option<Encoding> = None;
254}
255
256macro_rules! impl_static_encoding {
257 ($marker:ty, $encoding:expr) => {
258 impl private::Sealed for $marker {}
259
260 impl EncodingMarker for $marker {
261 const EXPECTED_ENCODING: Option<Encoding> = Some($encoding);
262 }
263
264 impl StaticEncoding for $marker {
265 const ENCODING: Encoding = $encoding;
266 }
267 };
268}
269
270impl_static_encoding!(encoding::Bincode, Encoding::Bincode);
271impl_static_encoding!(encoding::Json, Encoding::Json);
272impl_static_encoding!(encoding::Multipart, Encoding::Multipart);
273
274#[derive(Clone, Serialize, Deserialize, PartialEq, EnumAsInner)]
276enum Encoded {
277 Bincode(bytes::Bytes),
278 Json(bytes::Bytes),
279 Multipart(serde_multipart::Message),
280}
281
282impl Encoded {
283 pub fn len(&self) -> usize {
285 match &self {
286 Encoded::Bincode(data) => data.len(),
287 Encoded::Json(data) => data.len(),
288 Encoded::Multipart(message) => message.len(),
289 }
290 }
291
292 pub fn is_empty(&self) -> bool {
294 match &self {
295 Encoded::Bincode(data) => data.is_empty(),
296 Encoded::Json(data) => data.is_empty(),
297 Encoded::Multipart(message) => message.is_empty(),
298 }
299 }
300
301 pub fn encoding(&self) -> Encoding {
303 match &self {
304 Encoded::Bincode(_) => Encoding::Bincode,
305 Encoded::Json(_) => Encoding::Json,
306 Encoded::Multipart(_) => Encoding::Multipart,
307 }
308 }
309
310 pub fn crc(&self) -> u32 {
312 match &self {
313 Encoded::Bincode(data) => crc32fast::hash(data),
314 Encoded::Json(data) => crc32fast::hash(data),
315 Encoded::Multipart(message) => {
316 let mut hasher = crc32fast::Hasher::new();
317 for fragment in message.body().iter() {
318 hasher.update(fragment);
319 }
320 for part in message.parts() {
321 if let Some(typehash) = part.typehash() {
322 hasher.update(&typehash.to_be_bytes());
323 }
324 for fragment in part.iter() {
325 hasher.update(fragment);
326 }
327 }
328 hasher.finalize()
329 }
330 }
331 }
332}
333
334impl std::fmt::Debug for Encoded {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 match self {
337 Encoded::Bincode(data) => write!(f, "Encoded::Bincode({})", HexFmt(data)),
338 Encoded::Json(data) => write!(f, "Encoded::Json({})", HexFmt(data)),
339 Encoded::Multipart(message) => {
340 write!(
341 f,
342 "Encoded::Multipart(body={}",
343 HexFmt(&message.body().to_bytes())
344 )?;
345 for (index, part) in message.parts().iter().enumerate() {
346 if let Some(typehash) = part.typehash() {
347 write!(
348 f,
349 ", part[{}](typehash={})={}",
350 index,
351 typehash,
352 HexFmt(&part.to_bytes())
353 )?;
354 } else {
355 write!(f, ", part[{}]={}", index, HexFmt(&part.to_bytes()))?;
356 }
357 }
358 write!(f, ")")
359 }
360 }
361 }
362}
363
364#[derive(Debug, thiserror::Error)]
366pub enum Error {
367 #[error(transparent)]
369 BincodeEncode(#[from] bincode::error::EncodeError),
370
371 #[error(transparent)]
373 BincodeDecode(#[from] bincode::error::DecodeError),
374
375 #[error(transparent)]
377 Json(#[from] serde_json::Error),
378
379 #[error("unknown encoding: {0}")]
381 InvalidEncoding(String),
382
383 #[error("attempted to deserialize a broken Any value")]
385 BrokenAny,
386
387 #[error("type mismatch: expected {expected}, found {actual}")]
389 TypeMismatch {
390 expected: &'static str,
391 actual: String,
392 },
393
394 #[error("binary does not have typeinfo for typehash {0}")]
396 MissingTypeInfo(u64),
397
398 #[error("binary does not have dumper for typehash {0}")]
400 MissingDumper(u64),
401
402 #[error("only bincode encoding supports prefix operations")]
404 PrefixNotSupported,
405}
406
407pub type Result<T> = std::result::Result<T, Error>;
409
410#[derive(Clone, Debug, PartialEq)]
418pub struct Any<E: EncodingMarker = encoding::AnyEncoding> {
419 encoded: Encoded,
421 typehash: u64,
424 _encoding: PhantomData<E>,
425}
426
427impl<E: EncodingMarker> std::fmt::Display for Any<E> {
428 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
429 match self.dump() {
430 Ok(value) => {
431 let typename = self.typename().unwrap();
433 let basename = typename.split("::").last().unwrap_or(typename);
435 write!(f, "{}{}", basename, JsonFmt(&value))
436 }
437 Err(_) => write!(f, "{:?}", self.encoded),
438 }
439 }
440}
441
442impl<E: EncodingMarker> Serialize for Any<E> {
443 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
444 where
445 S: serde::Serializer,
446 {
447 let mut state = serializer.serialize_struct("Any", 2)?;
448 state.serialize_field("encoded", &self.encoded)?;
449 state.serialize_field("typehash", &self.typehash)?;
450 state.end()
451 }
452}
453
454#[derive(Deserialize)]
455struct AnyFields {
456 encoded: Encoded,
457 typehash: u64,
458}
459
460impl<'de, E: EncodingMarker> Deserialize<'de> for Any<E> {
461 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
462 where
463 D: serde::Deserializer<'de>,
464 {
465 let fields = AnyFields::deserialize(deserializer)?;
466 if let Some(expected) = E::EXPECTED_ENCODING {
467 let actual = fields.encoded.encoding();
468 if actual != expected {
469 return Err(serde::de::Error::custom(format!(
470 "expected {} encoding, found {}",
471 expected, actual
472 )));
473 }
474 }
475
476 Ok(Self {
477 encoded: fields.encoded,
478 typehash: fields.typehash,
479 _encoding: PhantomData,
480 })
481 }
482}
483
484fn encode_with_encoding<U: Serialize>(encoding: Encoding, value: &U) -> Result<Encoded> {
485 match encoding {
486 Encoding::Bincode => Ok(Encoded::Bincode(
487 bincode::serde::encode_to_vec(value, bincode::config::legacy())?.into(),
488 )),
489 Encoding::Json => Ok(Encoded::Json(serde_json::to_vec(value)?.into())),
490 Encoding::Multipart => Ok(Encoded::Multipart(
491 serde_multipart::serialize_bincode(value)
492 .map_err(|e| Error::InvalidEncoding(e.to_string()))?,
493 )),
494 }
495}
496
497impl Any {
498 pub fn serialize_with_encoding<T: Serialize + Named>(
500 encoding: Encoding,
501 value: &T,
502 ) -> Result<Self> {
503 Self::serialize_with_encoding_as::<T, T>(encoding, value)
504 }
505
506 pub fn serialize_with_encoding_as<T: Named, U: Serialize>(
510 encoding: Encoding,
511 value: &U,
512 ) -> Result<Self> {
513 Ok(Self {
514 encoded: encode_with_encoding(encoding, value)?,
515 typehash: T::typehash(),
516 _encoding: PhantomData,
517 })
518 }
519
520 pub fn new_broken() -> Self {
523 Self {
524 encoded: Encoded::Bincode(bytes::Bytes::new()),
525 typehash: BROKEN_TYPEHASH,
526 _encoding: PhantomData,
527 }
528 }
529
530 pub fn try_into_static_encoding<E: StaticEncoding>(self) -> std::result::Result<Any<E>, Self> {
532 if self.encoding() == E::ENCODING {
533 Ok(Any {
534 encoded: self.encoded,
535 typehash: self.typehash,
536 _encoding: PhantomData,
537 })
538 } else {
539 Err(self)
540 }
541 }
542
543 pub fn try_into_bincode(self) -> std::result::Result<Any<encoding::Bincode>, Self> {
545 self.try_into_static_encoding()
546 }
547
548 pub fn try_into_json(self) -> std::result::Result<Any<encoding::Json>, Self> {
550 self.try_into_static_encoding()
551 }
552
553 pub fn try_into_multipart(self) -> std::result::Result<Any<encoding::Multipart>, Self> {
555 self.try_into_static_encoding()
556 }
557}
558
559impl<E: EncodingMarker> Any<E> {
560 pub fn serialize<T: Serialize + Named>(value: &T) -> Result<Self> {
566 Self::serialize_as::<T, T>(value)
567 }
568
569 pub fn serialize_as<T: Named, U: Serialize>(value: &U) -> Result<Self> {
571 let encoding = E::EXPECTED_ENCODING
572 .unwrap_or_else(|| hyperactor_config::global::get(config::DEFAULT_ENCODING));
573 Ok(Self {
574 encoded: encode_with_encoding(encoding, value)?,
575 typehash: T::typehash(),
576 _encoding: PhantomData,
577 })
578 }
579
580 pub fn erase_encoding(self) -> Any {
582 Any {
583 encoded: self.encoded,
584 typehash: self.typehash,
585 _encoding: PhantomData,
586 }
587 }
588
589 pub fn is_broken(&self) -> bool {
591 self.typehash == BROKEN_TYPEHASH
592 }
593
594 pub fn deserialized<T: DeserializeOwned + Named>(&self) -> Result<T> {
596 if self.is_broken() {
597 return Err(Error::BrokenAny);
598 }
599 if !self.is::<T>() {
600 return Err(Error::TypeMismatch {
601 expected: T::typename(),
602 actual: self.typename().unwrap_or("unknown").to_string(),
603 });
604 }
605 self.deserialized_unchecked()
606 }
607
608 pub fn deserialized_unchecked<T: DeserializeOwned>(&self) -> Result<T> {
612 match &self.encoded {
613 Encoded::Bincode(data) => Ok(bincode::serde::decode_from_slice(
614 data,
615 bincode::config::legacy(),
616 )
617 .map(|(v, _)| v)?),
618 Encoded::Json(data) => Ok(serde_json::from_slice(data)?),
619 Encoded::Multipart(message) => {
620 Ok(serde_multipart::deserialize_bincode(message.clone())
621 .map_err(|e| Error::InvalidEncoding(e.to_string()))?)
622 }
623 }
624 }
625
626 pub fn transcode_to_json(self) -> std::result::Result<Any<encoding::Json>, Self> {
629 match self.encoded {
630 Encoded::Bincode(_) | Encoded::Multipart(_) => {
631 let json_value = match self.dump() {
632 Ok(json_value) => json_value,
633 Err(_) => return Err(self),
634 };
635 let json_data = match serde_json::to_vec(&json_value) {
636 Ok(json_data) => json_data,
637 Err(_) => return Err(self),
638 };
639 Ok(Any {
640 encoded: Encoded::Json(json_data.into()),
641 typehash: self.typehash,
642 _encoding: PhantomData,
643 })
644 }
645 Encoded::Json(_) => Ok(Any {
646 encoded: self.encoded,
647 typehash: self.typehash,
648 _encoding: PhantomData,
649 }),
650 }
651 }
652
653 pub fn dump(&self) -> Result<serde_json::Value> {
656 match &self.encoded {
657 Encoded::Bincode(_) | Encoded::Multipart(_) => {
658 let Some(typeinfo) = TYPE_INFO.get(&self.typehash) else {
659 return Err(Error::MissingTypeInfo(self.typehash));
660 };
661 typeinfo.dump(self.clone().erase_encoding())
662 }
663 Encoded::Json(data) => Ok(serde_json::from_slice(data)?),
664 }
665 }
666
667 pub fn encoding(&self) -> Encoding {
669 self.encoded.encoding()
670 }
671
672 pub fn typehash(&self) -> u64 {
674 self.typehash
675 }
676
677 pub fn typename(&self) -> Option<&'static str> {
679 TYPE_INFO
680 .get(&self.typehash)
681 .map(|typeinfo| typeinfo.typename())
682 }
683
684 pub fn prefix<T: DeserializeOwned>(&self) -> Result<T> {
689 match &self.encoded {
690 Encoded::Bincode(data) => Ok(bincode::serde::decode_from_slice(
691 data,
692 bincode::config::legacy(),
693 )
694 .map(|(v, _)| v)?),
695 _ => Err(Error::PrefixNotSupported),
696 }
697 }
698
699 pub fn emplace_prefix<T: Serialize + DeserializeOwned>(&mut self, prefix: T) -> Result<()> {
702 let data = match &self.encoded {
703 Encoded::Bincode(data) => data,
704 _ => return Err(Error::PrefixNotSupported),
705 };
706
707 let mut cursor = Cursor::new(data.clone());
712 let _prefix: T =
713 bincode::serde::decode_from_std_read(&mut cursor, bincode::config::legacy()).unwrap();
714 let position = cursor.position() as usize;
715 let suffix = &cursor.into_inner()[position..];
716 let mut data = bincode::serde::encode_to_vec(&prefix, bincode::config::legacy())?;
717 data.extend_from_slice(suffix);
718 self.encoded = Encoded::Bincode(data.into());
719
720 Ok(())
721 }
722
723 pub fn len(&self) -> usize {
725 self.encoded.len()
726 }
727
728 pub fn is_empty(&self) -> bool {
730 self.encoded.is_empty()
731 }
732
733 pub fn crc(&self) -> u32 {
735 self.encoded.crc()
736 }
737
738 pub fn is<M: Named>(&self) -> bool {
741 self.typehash == M::typehash()
742 }
743}
744
745impl Any<encoding::Multipart> {
746 pub fn visit_multipart_parts_mut<T, E>(
748 &mut self,
749 f: impl FnMut(&mut T) -> std::result::Result<(), E>,
750 ) -> std::result::Result<(), E>
751 where
752 T: Serialize + DeserializeOwned + Named,
753 E: From<serde_multipart::Error>,
754 {
755 let Encoded::Multipart(message) = &mut self.encoded else {
756 panic!(
757 "multipart Any contained {} encoding",
758 self.encoded.encoding()
759 );
760 };
761 message.visit_parts_mut(f)
762 }
763}
764
765const MAX_BYTE_PREVIEW_LENGTH: usize = 8;
766
767fn display_bytes_as_hash(f: &mut impl std::fmt::Write, bytes: &[u8]) -> std::fmt::Result {
768 let hash = crc32fast::hash(bytes);
769 write!(f, "CRC:{:x}", hash)?;
770 for &byte in bytes.iter().take(MAX_BYTE_PREVIEW_LENGTH) {
772 write!(f, " {:x}", byte)?;
773 }
774 if bytes.len() > MAX_BYTE_PREVIEW_LENGTH {
775 write!(f, " [...{} bytes]", bytes.len() - MAX_BYTE_PREVIEW_LENGTH)?;
776 }
777 Ok(())
778}
779
780pub struct HexFmt<'a>(pub &'a [u8]);
782
783impl std::fmt::Display for HexFmt<'_> {
784 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
785 display_bytes_as_hash(f, self.0)
787 }
788}
789
790pub struct JsonFmt<'a>(pub &'a serde_json::Value);
793
794const MAX_JSON_VALUE_DISPLAY_LENGTH: usize = 8;
795
796impl std::fmt::Display for JsonFmt<'_> {
797 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798 fn truncate_and_hash(value_str: &str) -> String {
801 let truncate_at = MAX_JSON_VALUE_DISPLAY_LENGTH.min(value_str.len());
802
803 let mut safe_truncate_at = truncate_at;
805 while safe_truncate_at > 0 && !value_str.is_char_boundary(safe_truncate_at) {
806 safe_truncate_at -= 1;
807 }
808
809 let truncated_str = &value_str[..safe_truncate_at];
810 let mut result = truncated_str.to_string();
811 result.push_str(&format!("[...{} chars] ", value_str.len()));
812 display_bytes_as_hash(&mut result, value_str.as_bytes()).unwrap();
813 result
814 }
815
816 fn truncate_json_values(value: &serde_json::Value) -> serde_json::Value {
818 match value {
819 serde_json::Value::String(s) => {
820 if s.len() > MAX_JSON_VALUE_DISPLAY_LENGTH {
821 serde_json::Value::String(truncate_and_hash(s))
822 } else {
823 value.clone()
824 }
825 }
826 serde_json::Value::Array(arr) => {
827 let array_str = serde_json::to_string(arr).unwrap();
828 if array_str.len() > MAX_JSON_VALUE_DISPLAY_LENGTH {
829 serde_json::Value::String(truncate_and_hash(&array_str))
830 } else {
831 value.clone()
832 }
833 }
834 serde_json::Value::Object(obj) => {
835 let truncated_obj: serde_json::Map<_, _> = obj
836 .iter()
837 .map(|(k, v)| (k.clone(), truncate_json_values(v)))
838 .collect();
839 serde_json::Value::Object(truncated_obj)
840 }
841 _ => value.clone(),
842 }
843 }
844
845 let truncated = truncate_json_values(self.0);
846 write!(f, "{}", truncated)
847 }
848}
849
850#[cfg(test)]
851mod tests {
852 use serde::Deserialize;
853 use serde::Serialize;
854 use serde_multipart::Part;
855 use strum::IntoEnumIterator;
856 use typeuri::Named;
857
858 use super::*;
859
860 #[derive(typeuri::Named, Serialize, Deserialize)]
861 struct TestStruct;
862
863 #[test]
864 fn test_names() {
865 assert_eq!(String::typename(), "String");
866 assert_eq!(Option::<String>::typename(), "Option<String>");
867 assert_eq!(Vec::<String>::typename(), "Vec<String>");
868 assert_eq!(Vec::<Vec::<String>>::typename(), "Vec<Vec<String>>");
869 assert_eq!(
870 Vec::<Vec::<Vec::<String>>>::typename(),
871 "Vec<Vec<Vec<String>>>"
872 );
873 assert_eq!(
874 <(u64, String, Option::<isize>)>::typename(),
875 "(u64, String, Option<isize>)"
876 );
877 assert_eq!(TestStruct::typename(), "wirevalue::tests::TestStruct");
878 assert_eq!(
879 Vec::<TestStruct>::typename(),
880 "Vec<wirevalue::tests::TestStruct>"
881 );
882 }
883
884 #[test]
885 fn test_ports() {
886 assert_eq!(String::typehash(), 3947244799002047352u64);
887 assert_eq!(String::port(), String::typehash());
888 assert_ne!(
889 Vec::<Vec::<Vec::<String>>>::typehash(),
890 Vec::<Vec::<Vec::<Vec::<String>>>>::typehash(),
891 );
892 }
893
894 #[derive(typeuri::Named, Serialize, Deserialize, PartialEq, Eq, Debug)]
895 struct TestDumpStruct {
896 a: String,
897 b: u64,
898 c: Option<i32>,
899 d: Option<Part>,
900 }
901 crate::register_type!(TestDumpStruct);
902
903 #[test]
904 fn test_dump_struct() {
905 let data = TestDumpStruct {
906 a: "hello".to_string(),
907 b: 1234,
908 c: Some(5678),
909 d: None,
910 };
911 let serialized: Any = Any::serialize(&data).unwrap();
912 let serialized_json = serialized.clone().transcode_to_json().unwrap();
913
914 assert!(serialized.encoded.is_multipart());
915 assert!(serialized_json.encoded.is_json());
916
917 let json_string =
918 String::from_utf8(serialized_json.encoded.as_json().unwrap().to_vec().clone()).unwrap();
919 assert_eq!(
921 json_string,
922 "{\"a\":\"hello\",\"b\":1234,\"c\":5678,\"d\":null}"
923 );
924
925 for serialized in [serialized, serialized_json.erase_encoding()] {
926 assert_eq!(
929 serialized.typename(),
930 Some("wirevalue::tests::TestDumpStruct")
931 );
932
933 let json = serialized.dump().unwrap();
934 assert_eq!(
935 json,
936 serde_json::json!({
937 "a": "hello",
938 "b": 1234,
939 "c": 5678,
940 "d": null,
941 })
942 );
943
944 assert_eq!(
945 format!("{}", serialized),
946 "TestDumpStruct{\"a\":\"hello\",\"b\":1234,\"c\":5678,\"d\":null}",
947 );
948 }
949 }
950
951 #[test]
952 fn test_emplace_prefix() {
953 let config = hyperactor_config::global::lock();
954 let _guard = config.override_key(config::DEFAULT_ENCODING, Encoding::Bincode);
955 let data = TestDumpStruct {
956 a: "hello".to_string(),
957 b: 1234,
958 c: Some(5678),
959 d: None,
960 };
961
962 let mut ser: Any = Any::serialize(&data).unwrap();
963 assert_eq!(ser.prefix::<String>().unwrap(), "hello".to_string());
964
965 ser.emplace_prefix("hello, world, 123!".to_string())
966 .unwrap();
967
968 assert_eq!(
969 ser.deserialized::<TestDumpStruct>().unwrap(),
970 TestDumpStruct {
971 a: "hello, world, 123!".to_string(),
972 b: 1234,
973 c: Some(5678),
974 d: None,
975 }
976 );
977 }
978
979 #[test]
980 fn test_arms() {
981 #[derive(typeuri::Named, Serialize, Deserialize)]
982 enum TestArm {
983 #[allow(dead_code)]
984 A(u32),
985 B,
986 C(),
987 D {
988 #[allow(dead_code)]
989 a: u32,
990 #[allow(dead_code)]
991 b: String,
992 },
993 }
994
995 assert_eq!(TestArm::A(1234).arm(), Some("A"));
996 assert_eq!(TestArm::B.arm(), Some("B"));
997 assert_eq!(TestArm::C().arm(), Some("C"));
998 assert_eq!(
999 TestArm::D {
1000 a: 1234,
1001 b: "hello".to_string()
1002 }
1003 .arm(),
1004 Some("D")
1005 );
1006 }
1007
1008 #[test]
1009 fn display_hex() {
1010 assert_eq!(
1011 format!("{}", HexFmt("hello world".as_bytes())),
1012 "CRC:d4a1185 68 65 6c 6c 6f 20 77 6f [...3 bytes]"
1013 );
1014 assert_eq!(format!("{}", HexFmt("".as_bytes())), "CRC:0");
1015 assert_eq!(
1016 format!("{}", HexFmt("a very long string that is long".as_bytes())),
1017 "CRC:c7e24f62 61 20 76 65 72 79 20 6c [...23 bytes]"
1018 );
1019 }
1020
1021 #[test]
1022 fn test_json_fmt() {
1023 let json_value = serde_json::json!({
1024 "name": "test",
1025 "number": 42,
1026 "nested": {
1027 "key": "value"
1028 }
1029 });
1030 assert_eq!(
1032 format!("{}", JsonFmt(&json_value)),
1033 "{\"name\":\"test\",\"nested\":{\"key\":\"value\"},\"number\":42}",
1034 );
1035
1036 let empty_json = serde_json::json!({});
1037 assert_eq!(format!("{}", JsonFmt(&empty_json)), "{}");
1038
1039 let simple_array = serde_json::json!([1, 2, 3]);
1040 assert_eq!(format!("{}", JsonFmt(&simple_array)), "[1,2,3]");
1041
1042 let long_string_json = serde_json::json!({
1044 "long_string": "a".repeat(MAX_JSON_VALUE_DISPLAY_LENGTH * 5)
1045 });
1046 assert_eq!(
1047 format!("{}", JsonFmt(&long_string_json)),
1048 "{\"long_string\":\"aaaaaaaa[...40 chars] CRC:c95b8a25 61 61 61 61 61 61 61 61 [...32 bytes]\"}"
1049 );
1050
1051 let long_array_json =
1053 serde_json::json!((1..=(MAX_JSON_VALUE_DISPLAY_LENGTH + 4)).collect::<Vec<_>>());
1054 assert_eq!(
1055 format!("{}", JsonFmt(&long_array_json)),
1056 "\"[1,2,3,4[...28 chars] CRC:e5c881af 5b 31 2c 32 2c 33 2c 34 [...20 bytes]\""
1057 );
1058
1059 let nested_json = serde_json::json!({
1061 "simple_number": 42,
1062 "simple_bool": true,
1063 "outer": {
1064 "long_string": "a".repeat(MAX_JSON_VALUE_DISPLAY_LENGTH + 10),
1065 "long_array": (1..=(MAX_JSON_VALUE_DISPLAY_LENGTH + 4)).collect::<Vec<_>>(),
1066 "inner": {
1067 "simple_value": "short",
1068 }
1069 }
1070 });
1071 println!("{}", JsonFmt(&nested_json));
1072 assert_eq!(
1073 format!("{}", JsonFmt(&nested_json)),
1074 "{\"outer\":{\"inner\":{\"simple_value\":\"short\"},\"long_array\":\"[1,2,3,4[...28 chars] CRC:e5c881af 5b 31 2c 32 2c 33 2c 34 [...20 bytes]\",\"long_string\":\"aaaaaaaa[...18 chars] CRC:b8ac0e31 61 61 61 61 61 61 61 61 [...10 bytes]\"},\"simple_bool\":true,\"simple_number\":42}",
1075 );
1076 }
1077
1078 #[test]
1079 fn test_json_fmt_utf8_truncation() {
1080 let utf8_json = serde_json::json!({
1085 "emoji": "1234567🦀" });
1087
1088 let result = format!("{}", JsonFmt(&utf8_json));
1090
1091 assert!(result.contains("1234567"));
1093 assert!(!result.contains("🦀")); let all_multibyte = serde_json::json!({
1097 "chinese": "ä½ å¥½ä¸–ç•Œ" });
1099 let result3 = format!("{}", JsonFmt(&all_multibyte));
1100 assert!(!result3.is_empty());
1101 }
1102
1103 #[test]
1104 fn test_encodings() {
1105 let value = TestDumpStruct {
1106 a: "hello, world".to_string(),
1107 b: 123,
1108 c: Some(321),
1109 d: Some(Part::from("hello, world, again")),
1110 };
1111 for enc in Encoding::iter() {
1112 let ser = Any::serialize_with_encoding(enc, &value).unwrap();
1113 assert_eq!(ser.encoding(), enc);
1114 assert_eq!(ser.deserialized::<TestDumpStruct>().unwrap(), value);
1115 }
1116 }
1117
1118 #[test]
1119 fn test_static_multipart_any() {
1120 let value = TestDumpStruct {
1121 a: "hello, multipart".to_string(),
1122 b: 456,
1123 c: Some(654),
1124 d: Some(Part::from("part")),
1125 };
1126
1127 let multipart = Any::<encoding::Multipart>::serialize(&value).unwrap();
1128 assert_eq!(multipart.encoding(), Encoding::Multipart);
1129 assert_eq!(multipart.deserialized::<TestDumpStruct>().unwrap(), value);
1130
1131 let erased = multipart.erase_encoding();
1132 let multipart = erased.try_into_multipart().unwrap();
1133 assert_eq!(multipart.encoding(), Encoding::Multipart);
1134 }
1135
1136 #[test]
1137 fn test_static_encoding_deserialize_rejects_mismatch() {
1138 let value = TestDumpStruct {
1139 a: "hello, bincode".to_string(),
1140 b: 789,
1141 c: None,
1142 d: None,
1143 };
1144 let bincode = Any::<encoding::Bincode>::serialize(&value).unwrap();
1145 let serialized = serde_json::to_vec(&bincode.erase_encoding()).unwrap();
1146
1147 let err = serde_json::from_slice::<Any<encoding::Multipart>>(&serialized).unwrap_err();
1148 assert!(err.to_string().contains("expected serde_multipart"));
1149 }
1150
1151 #[test]
1152 fn test_broken_any() {
1153 let broken = Any::new_broken();
1154 assert!(broken.is_broken());
1155 assert_eq!(broken.typehash(), BROKEN_TYPEHASH);
1156
1157 let normal: Any = Any::serialize(&"hello".to_string()).unwrap();
1159 assert!(!normal.is_broken());
1160
1161 let err = broken.deserialized::<String>().unwrap_err();
1163 assert!(err.to_string().contains("broken"));
1164 }
1165}