1#![cfg_attr(not(feature = "wire-implementer-api"), allow(dead_code))]
15
16#[cfg(any(feature = "protobuf-support", feature = "up-core-api"))]
31use std::io::Read;
32#[cfg(feature = "selected-wire-codec-core")]
33use std::{error::Error, fmt::Display};
34
35#[cfg(feature = "protobuf-support")]
36use crate::payload::codec::{PayloadFormat, PayloadLayout, ProtobufPayload};
37#[cfg(feature = "protobuf-support")]
38use crate::payload::UWireError;
39use crate::payload::{
40 codec::{DecodePayload, EncodePayload, PayloadCodec, ReadDecodePayload},
41 stable::{StableContainerPayload, StablePayload},
42};
43#[cfg(any(feature = "protobuf-support", feature = "up-core-api"))]
44use crate::PayloadEncoding;
45#[cfg(feature = "protobuf-support")]
46use crate::ProtobufMappable;
47#[cfg(feature = "selected-wire-codec-core")]
48use crate::{UCode, UFrameMetadata, UFrameMetadataError, UStatus};
49
50#[cfg(feature = "selected-wire-codec-core")]
51const NATIVE_PREFIX_MAGIC: &[u8; 4] = b"UPWM";
52#[cfg(feature = "selected-wire-codec-core")]
53const ID_REF_COMPACT: u8 = 0x00;
54#[cfg(feature = "selected-wire-codec-core")]
55const ID_REF_LITERAL: u8 = 0x01;
56
57pub const FORMAT_VERSION: u16 = 1;
59
60pub const INVALID_ID_FIXTURE_COMPACT_ID: u16 = 0xFFFF;
62
63pub const LOCAL_EXPERIMENTAL_COMPACT_ID_START: u16 = 0x8000;
65
66pub const LOCAL_EXPERIMENTAL_COMPACT_ID_END: u16 = 0xFFFE;
68
69pub const UPROTOCOL_NATIVE_WIRE_ID: WireIdentity =
71 WireIdentity::new("org.eclipse.uprotocol.wire.native", 0x0001);
72
73pub const NATIVE_EXPLICIT_PAYLOAD_FAMILY_ID: WireIdentity =
75 WireIdentity::new("native-explicit", 0x0001);
76
77pub const PROTOBUF_WIRE_ID: WireIdentity =
79 WireIdentity::new("org.eclipse.uprotocol.wire.protobuf", 0x0002);
80
81pub const PROTOBUF_PAYLOAD_FAMILY_ID: WireIdentity = WireIdentity::new("protobuf", 0x0002);
83
84pub const XCDR_V2_WIRE_ID: WireIdentity =
89 WireIdentity::new("org.eclipse.uprotocol.wire.xcdr-v2", 0x0003);
90
91pub const XCDR_V2_PAYLOAD_FAMILY_ID: WireIdentity = WireIdentity::new("xcdr-v2", 0x0003);
93
94pub const STABLE_CONTAINER_WIRE_ID: WireIdentity =
96 WireIdentity::new("org.eclipse.uprotocol.wire.stable-container", 0x0004);
97
98pub const STABLE_CONTAINER_PAYLOAD_FAMILY_ID: WireIdentity =
100 WireIdentity::new("stable-container", 0x0004);
101
102pub const NATIVE_PREFIX_METADATA_LAYOUT_ID: WireIdentity =
104 WireIdentity::new("org.eclipse.uprotocol.metadata.native-prefix", 0x0001);
105
106pub const UFRAME_FIELDS_METADATA_LAYOUT_ID: WireIdentity =
115 WireIdentity::new("org.eclipse.uprotocol.metadata.uframe-fields", 0x0002);
116
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub struct WireIdentity {
120 literal_id: &'static str,
121 compact_id: u16,
122}
123
124impl WireIdentity {
125 #[must_use]
127 pub const fn new(literal_id: &'static str, compact_id: u16) -> Self {
128 Self {
129 literal_id,
130 compact_id,
131 }
132 }
133
134 #[must_use]
136 pub fn literal_id(self) -> &'static str {
137 self.literal_id
138 }
139
140 #[must_use]
142 pub fn compact_id(self) -> u16 {
143 self.compact_id
144 }
145}
146
147#[derive(Clone, Debug, Eq, PartialEq)]
149pub enum WireIdentityRef {
150 Compact(u16),
152 Literal(String),
154}
155
156impl WireIdentityRef {
157 #[must_use]
159 pub fn matches(&self, expected: WireIdentity) -> bool {
160 match self {
161 Self::Compact(actual) => *actual == expected.compact_id(),
162 Self::Literal(actual) => actual == expected.literal_id(),
163 }
164 }
165}
166
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
169pub enum WireCompatibility {
170 Compatible,
172 Incompatible,
174}
175
176impl WireCompatibility {
177 #[must_use]
179 pub fn is_compatible(self) -> bool {
180 matches!(self, Self::Compatible)
181 }
182}
183
184pub trait UWire {
186 const WIRE_ID: WireIdentity;
188 const PAYLOAD_FAMILY_ID: WireIdentity;
190 const METADATA_LAYOUT_ID: WireIdentity;
192 const FORMAT_VERSION: u16;
194
195 fn wire_compatibility(actual: &WireIdentityRef) -> WireCompatibility {
197 if actual.matches(Self::WIRE_ID) {
198 WireCompatibility::Compatible
199 } else {
200 WireCompatibility::Incompatible
201 }
202 }
203
204 fn payload_family_compatibility(actual: &WireIdentityRef) -> WireCompatibility {
206 if actual.matches(Self::PAYLOAD_FAMILY_ID) {
207 WireCompatibility::Compatible
208 } else {
209 WireCompatibility::Incompatible
210 }
211 }
212
213 #[cfg(feature = "selected-wire-codec-core")]
215 fn metadata_context() -> UWireMetadataContext
216 where
217 Self: Sized,
218 {
219 UWireMetadataContext::from_wire::<Self>()
220 }
221}
222
223#[cfg(feature = "selected-wire-codec-core")]
225#[derive(Clone, Copy, Debug)]
226pub struct UWireMetadataContext {
227 pub wire_id: WireIdentity,
229 pub payload_family_id: WireIdentity,
231 pub metadata_layout_id: WireIdentity,
233 pub format_version: u16,
235 wire_compatibility: Option<fn(&WireIdentityRef) -> WireCompatibility>,
236 payload_family_compatibility: Option<fn(&WireIdentityRef) -> WireCompatibility>,
237}
238
239#[cfg(feature = "selected-wire-codec-core")]
240impl UWireMetadataContext {
241 #[must_use]
243 pub fn from_wire<W>() -> Self
244 where
245 W: UWire,
246 {
247 Self {
248 wire_id: W::WIRE_ID,
249 payload_family_id: W::PAYLOAD_FAMILY_ID,
250 metadata_layout_id: W::METADATA_LAYOUT_ID,
251 format_version: W::FORMAT_VERSION,
252 wire_compatibility: Some(W::wire_compatibility),
253 payload_family_compatibility: Some(W::payload_family_compatibility),
254 }
255 }
256
257 #[must_use]
259 pub fn new_exact(
260 wire_id: WireIdentity,
261 payload_family_id: WireIdentity,
262 metadata_layout_id: WireIdentity,
263 format_version: u16,
264 ) -> Self {
265 Self {
266 wire_id,
267 payload_family_id,
268 metadata_layout_id,
269 format_version,
270 wire_compatibility: None,
271 payload_family_compatibility: None,
272 }
273 }
274
275 #[must_use]
281 pub fn with_metadata_layout(mut self, metadata_layout_id: WireIdentity) -> Self {
282 self.metadata_layout_id = metadata_layout_id;
283 self
284 }
285
286 #[must_use]
288 pub fn wire_compatibility(&self, actual: &WireIdentityRef) -> WireCompatibility {
289 if let Some(wire_compatibility) = self.wire_compatibility {
290 return wire_compatibility(actual);
291 }
292 if actual.matches(self.wire_id) {
293 WireCompatibility::Compatible
294 } else {
295 WireCompatibility::Incompatible
296 }
297 }
298
299 #[must_use]
301 pub fn payload_family_compatibility(&self, actual: &WireIdentityRef) -> WireCompatibility {
302 if let Some(payload_family_compatibility) = self.payload_family_compatibility {
303 return payload_family_compatibility(actual);
304 }
305 if actual.matches(self.payload_family_id) {
306 WireCompatibility::Compatible
307 } else {
308 WireCompatibility::Incompatible
309 }
310 }
311}
312
313#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
315pub struct UProtocolNativeWire;
316
317impl UWire for UProtocolNativeWire {
318 const WIRE_ID: WireIdentity = UPROTOCOL_NATIVE_WIRE_ID;
319 const PAYLOAD_FAMILY_ID: WireIdentity = NATIVE_EXPLICIT_PAYLOAD_FAMILY_ID;
320 const METADATA_LAYOUT_ID: WireIdentity = NATIVE_PREFIX_METADATA_LAYOUT_ID;
321 const FORMAT_VERSION: u16 = FORMAT_VERSION;
322}
323
324#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
326pub struct ProtobufWire;
327
328impl UWire for ProtobufWire {
329 const WIRE_ID: WireIdentity = PROTOBUF_WIRE_ID;
330 const PAYLOAD_FAMILY_ID: WireIdentity = PROTOBUF_PAYLOAD_FAMILY_ID;
331 const METADATA_LAYOUT_ID: WireIdentity = NATIVE_PREFIX_METADATA_LAYOUT_ID;
332 const FORMAT_VERSION: u16 = FORMAT_VERSION;
333}
334
335#[cfg(feature = "protobuf-support")]
336impl PayloadFormat for ProtobufWire {
337 fn name() -> &'static str {
338 ProtobufPayload::name()
339 }
340
341 fn encoding() -> PayloadEncoding {
342 ProtobufPayload::encoding()
343 }
344}
345
346#[cfg(feature = "protobuf-support")]
347impl<T> EncodePayload<T> for ProtobufWire
348where
349 T: ProtobufMappable,
350{
351 fn payload_layout(value: &T) -> Result<PayloadLayout, UWireError> {
352 ProtobufPayload::payload_layout(value)
353 }
354
355 fn encode_payload(value: &T, dst: &mut [u8]) -> Result<(), UWireError> {
356 ProtobufPayload::encode_payload(value, dst)
357 }
358}
359
360#[cfg(feature = "protobuf-support")]
361impl<'a, T> DecodePayload<'a, T> for ProtobufWire
362where
363 T: ProtobufMappable,
364{
365 fn decode_payload(src: &'a [u8]) -> Result<T, UWireError> {
366 ProtobufPayload::decode_payload(src)
367 }
368}
369
370#[cfg(feature = "protobuf-support")]
371impl<T> ReadDecodePayload<T> for ProtobufWire
372where
373 T: ProtobufMappable,
374{
375 fn decode_payload_from_reader<R: Read>(reader: R, payload_len: usize) -> Result<T, UWireError> {
376 ProtobufPayload::decode_payload_from_reader(reader, payload_len)
377 }
378}
379
380#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
382pub struct StableContainerWireFormat;
383
384impl UWire for StableContainerWireFormat {
385 const WIRE_ID: WireIdentity = STABLE_CONTAINER_WIRE_ID;
386 const PAYLOAD_FAMILY_ID: WireIdentity = STABLE_CONTAINER_PAYLOAD_FAMILY_ID;
387 const METADATA_LAYOUT_ID: WireIdentity = NATIVE_PREFIX_METADATA_LAYOUT_ID;
388 const FORMAT_VERSION: u16 = FORMAT_VERSION;
389}
390
391#[cfg(feature = "selected-wire-codec-core")]
393pub trait UWireMetadataCodec {
394 const METADATA_LAYOUT_ID: WireIdentity = NATIVE_PREFIX_METADATA_LAYOUT_ID;
402
403 fn encode_frame_metadata(
409 &self,
410 context: UWireMetadataContext,
411 metadata: &UFrameMetadata,
412 ) -> Result<Vec<u8>, UWireMetadataError>;
413
414 fn decode_frame_metadata(
421 &self,
422 context: UWireMetadataContext,
423 src: &[u8],
424 ) -> Result<UFrameMetadata, UWireMetadataError>;
425}
426
427#[cfg(feature = "selected-wire-codec-core")]
433pub trait UWireMetadataCodecFor<W>: UWireMetadataCodec
434where
435 W: UWire,
436{
437}
438
439#[cfg(feature = "selected-wire-codec-core")]
449#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
450pub struct NativePrefixFrameMetadataCodec;
451
452#[cfg(feature = "selected-wire-codec-core")]
453impl UWireMetadataCodec for NativePrefixFrameMetadataCodec {
454 const METADATA_LAYOUT_ID: WireIdentity = UFRAME_FIELDS_METADATA_LAYOUT_ID;
455
456 fn encode_frame_metadata(
457 &self,
458 context: UWireMetadataContext,
459 metadata: &UFrameMetadata,
460 ) -> Result<Vec<u8>, UWireMetadataError> {
461 let context = context.with_metadata_layout(Self::METADATA_LAYOUT_ID);
462 let fields = crate::frame::codec::encode_frame_metadata_fields(metadata)
463 .map_err(|error| UWireMetadataError::FrameMetadata(error.to_string()))?;
464 let mut out = Vec::with_capacity(
465 NATIVE_PREFIX_MAGIC.len()
466 + (3 * (1 + std::mem::size_of::<u16>()))
467 + (2 * std::mem::size_of::<u16>())
468 + std::mem::size_of::<u32>()
469 + fields.len(),
470 );
471 out.extend_from_slice(NATIVE_PREFIX_MAGIC);
472 write_identity_ref(&mut out, context.metadata_layout_id);
473 write_u16(&mut out, context.format_version);
474 write_identity_ref(&mut out, context.wire_id);
475 write_identity_ref(&mut out, context.payload_family_id);
476 write_u16(&mut out, 0);
477 write_len_prefixed_bytes(&mut out, &fields)?;
478 Ok(out)
479 }
480
481 fn decode_frame_metadata(
482 &self,
483 context: UWireMetadataContext,
484 src: &[u8],
485 ) -> Result<UFrameMetadata, UWireMetadataError> {
486 let context = context.with_metadata_layout(Self::METADATA_LAYOUT_ID);
487 let mut reader = MetadataReader::new(src);
488 read_and_check_native_prefix(&mut reader, context)?;
489 let fields = reader.read_len_prefixed_bytes()?;
490 reader.finish()?;
491 crate::frame::codec::decode_frame_metadata_fields(fields)
492 .map_err(|error| UWireMetadataError::MalformedMetadata(error.to_string()))
493 }
494}
495
496#[cfg(feature = "selected-wire-codec-core")]
497impl<W> UWireMetadataCodecFor<W> for NativePrefixFrameMetadataCodec where W: UWire {}
498
499pub trait UWireEncode<T: ?Sized>: EncodePayload<T> {}
501
502impl<C, T: ?Sized> UWireEncode<T> for C where C: EncodePayload<T> {}
503
504pub trait UWireDecode<'a, T>: DecodePayload<'a, T> {}
506
507impl<'a, C, T> UWireDecode<'a, T> for C where C: DecodePayload<'a, T> {}
508
509pub trait UWireDecodeOwned<T>: for<'a> DecodePayload<'a, T> {}
511
512impl<C, T> UWireDecodeOwned<T> for C where C: for<'a> DecodePayload<'a, T> {}
513
514pub trait UWireReadDecode<T>: ReadDecodePayload<T> {}
516
517impl<C, T> UWireReadDecode<T> for C where C: ReadDecodePayload<T> {}
518
519pub trait UWirePayload<T>: UWire {
529 type Codec: PayloadCodec;
531}
532
533impl<T> UWirePayload<T> for StableContainerWireFormat
534where
535 T: StablePayload,
536{
537 type Codec = StableContainerPayload<T>;
538}
539
540#[cfg(feature = "protobuf-support")]
541impl<T> UWirePayload<T> for ProtobufWire
542where
543 T: ProtobufMappable,
544{
545 type Codec = ProtobufPayload;
546}
547
548#[cfg(feature = "selected-wire-codec-core")]
550#[derive(Clone, Debug, Eq, PartialEq)]
551#[non_exhaustive]
552pub enum UWireMetadataError {
553 WrongMagic,
555 UnknownMetadataLayoutId {
557 actual: WireIdentityRef,
559 },
560 UnsupportedVersion {
563 expected: u16,
565 actual: u16,
567 },
568 WrongWireMetadata {
570 expected: WireIdentity,
572 actual: WireIdentityRef,
574 },
575 PayloadFamilyMismatch {
577 expected: WireIdentity,
579 actual: WireIdentityRef,
581 },
582 UnsupportedReservedFlags(u16),
584 UnsupportedPayloadEncoding(String),
586 MalformedMetadata(String),
588 FrameMetadata(String),
590 SerializationError(String),
592}
593
594#[cfg(feature = "selected-wire-codec-core")]
595impl Display for UWireMetadataError {
596 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
597 match self {
598 Self::WrongMagic => f.write_str("wrong native-prefix metadata magic"),
599 Self::UnknownMetadataLayoutId { actual } => {
600 write!(f, "unknown metadata layout id: {actual:?}")
601 }
602 Self::UnsupportedVersion { expected, actual } => write!(
603 f,
604 "unsupported metadata version: expected {expected}, got {actual}"
605 ),
606 Self::WrongWireMetadata { expected, actual } => write!(
607 f,
608 "wrong selected wire metadata: expected {expected:?}, got {actual:?}"
609 ),
610 Self::PayloadFamilyMismatch { expected, actual } => write!(
611 f,
612 "payload family mismatch: expected {expected:?}, got {actual:?}"
613 ),
614 Self::UnsupportedReservedFlags(flags) => {
615 write!(f, "unsupported native-prefix reserved flags: {flags:#06x}")
616 }
617 Self::UnsupportedPayloadEncoding(message) => {
618 write!(f, "unsupported payload encoding: {message}")
619 }
620 Self::MalformedMetadata(message) => write!(f, "malformed wire metadata: {message}"),
621 Self::FrameMetadata(message) => write!(f, "invalid frame metadata: {message}"),
622 Self::SerializationError(message) => {
623 write!(f, "metadata serialization error: {message}")
624 }
625 }
626 }
627}
628
629#[cfg(feature = "selected-wire-codec-core")]
630impl Error for UWireMetadataError {}
631
632#[cfg(feature = "selected-wire-codec-core")]
633impl From<UFrameMetadataError> for UWireMetadataError {
634 fn from(value: UFrameMetadataError) -> Self {
635 Self::FrameMetadata(value.to_string())
636 }
637}
638
639#[cfg(feature = "selected-wire-codec-core")]
640impl From<UWireMetadataError> for UStatus {
641 fn from(value: UWireMetadataError) -> Self {
642 UStatus::fail_with_code(UCode::InvalidArgument, value.to_string())
643 }
644}
645
646#[cfg(feature = "selected-wire-codec-core")]
649fn read_and_check_native_prefix(
650 reader: &mut MetadataReader<'_>,
651 context: UWireMetadataContext,
652) -> Result<(), UWireMetadataError> {
653 if reader.take(NATIVE_PREFIX_MAGIC.len())? != NATIVE_PREFIX_MAGIC.as_slice() {
654 return Err(UWireMetadataError::WrongMagic);
655 }
656
657 let layout_id = reader.read_identity_ref()?;
658 if !layout_id.matches(context.metadata_layout_id) {
659 return Err(UWireMetadataError::UnknownMetadataLayoutId { actual: layout_id });
660 }
661
662 let version = reader.read_u16()?;
663 if version != context.format_version {
664 return Err(UWireMetadataError::UnsupportedVersion {
665 expected: context.format_version,
666 actual: version,
667 });
668 }
669
670 let wire_id = reader.read_identity_ref()?;
671 if !context.wire_compatibility(&wire_id).is_compatible() {
672 return Err(UWireMetadataError::WrongWireMetadata {
673 expected: context.wire_id,
674 actual: wire_id,
675 });
676 }
677
678 let payload_family_id = reader.read_identity_ref()?;
679 if !context
680 .payload_family_compatibility(&payload_family_id)
681 .is_compatible()
682 {
683 return Err(UWireMetadataError::PayloadFamilyMismatch {
684 expected: context.payload_family_id,
685 actual: payload_family_id,
686 });
687 }
688
689 let flags = reader.read_u16()?;
690 if flags != 0 {
691 return Err(UWireMetadataError::UnsupportedReservedFlags(flags));
692 }
693 Ok(())
694}
695
696#[cfg(feature = "selected-wire-codec-core")]
697fn write_identity_ref(out: &mut Vec<u8>, identity: WireIdentity) {
698 out.push(ID_REF_COMPACT);
699 write_u16(out, identity.compact_id());
700}
701
702#[cfg(feature = "selected-wire-codec-core")]
703fn write_len_prefixed_bytes(out: &mut Vec<u8>, value: &[u8]) -> Result<(), UWireMetadataError> {
704 let len = u32::try_from(value.len()).map_err(|_| {
705 UWireMetadataError::MalformedMetadata(format!(
706 "length {} exceeds u32 native-prefix limit",
707 value.len()
708 ))
709 })?;
710 write_u32(out, len);
711 out.extend_from_slice(value);
712 Ok(())
713}
714
715#[cfg(feature = "selected-wire-codec-core")]
716fn write_u16(out: &mut Vec<u8>, value: u16) {
717 out.extend_from_slice(&value.to_le_bytes());
718}
719
720#[cfg(feature = "selected-wire-codec-core")]
721fn write_u32(out: &mut Vec<u8>, value: u32) {
722 out.extend_from_slice(&value.to_le_bytes());
723}
724
725#[cfg(feature = "selected-wire-codec-core")]
726struct MetadataReader<'a> {
727 src: &'a [u8],
728 pos: usize,
729}
730
731#[cfg(feature = "selected-wire-codec-core")]
732impl<'a> MetadataReader<'a> {
733 fn new(src: &'a [u8]) -> Self {
734 Self { src, pos: 0 }
735 }
736
737 fn finish(&self) -> Result<(), UWireMetadataError> {
738 if self.pos == self.src.len() {
739 Ok(())
740 } else {
741 Err(UWireMetadataError::MalformedMetadata(format!(
742 "{} trailing byte(s)",
743 self.src.len() - self.pos
744 )))
745 }
746 }
747
748 fn take(&mut self, len: usize) -> Result<&'a [u8], UWireMetadataError> {
749 let end = self.pos.checked_add(len).ok_or_else(|| {
750 UWireMetadataError::MalformedMetadata("metadata length overflow".to_string())
751 })?;
752 let chunk = self.src.get(self.pos..end).ok_or_else(|| {
753 UWireMetadataError::MalformedMetadata(format!(
754 "needed {len} byte(s) at offset {}, input has {} byte(s)",
755 self.pos,
756 self.src.len()
757 ))
758 })?;
759 self.pos = end;
760 Ok(chunk)
761 }
762
763 fn read_u8(&mut self) -> Result<u8, UWireMetadataError> {
764 let bytes = self.take(1)?;
765 bytes
766 .first()
767 .copied()
768 .ok_or_else(|| UWireMetadataError::MalformedMetadata("missing u8 field".to_string()))
769 }
770
771 fn read_u16(&mut self) -> Result<u16, UWireMetadataError> {
772 let bytes = self.take(2)?;
773 let array = <[u8; 2]>::try_from(bytes)
774 .map_err(|_| UWireMetadataError::MalformedMetadata("invalid u16 field".to_string()))?;
775 Ok(u16::from_le_bytes(array))
776 }
777
778 fn read_u32(&mut self) -> Result<u32, UWireMetadataError> {
779 let bytes = self.take(4)?;
780 let array = <[u8; 4]>::try_from(bytes)
781 .map_err(|_| UWireMetadataError::MalformedMetadata("invalid u32 field".to_string()))?;
782 Ok(u32::from_le_bytes(array))
783 }
784
785 fn read_identity_ref(&mut self) -> Result<WireIdentityRef, UWireMetadataError> {
786 match self.read_u8()? {
787 ID_REF_COMPACT => Ok(WireIdentityRef::Compact(self.read_u16()?)),
788 ID_REF_LITERAL => {
789 let bytes = self.read_len_prefixed_bytes()?;
790 let value = std::str::from_utf8(bytes).map_err(|error| {
791 UWireMetadataError::MalformedMetadata(format!(
792 "identity literal is not UTF-8: {error}"
793 ))
794 })?;
795 Ok(WireIdentityRef::Literal(value.to_string()))
796 }
797 tag => Err(UWireMetadataError::MalformedMetadata(format!(
798 "unknown identity reference tag {tag}"
799 ))),
800 }
801 }
802
803 fn read_len_prefixed_bytes(&mut self) -> Result<&'a [u8], UWireMetadataError> {
804 let len = usize::try_from(self.read_u32()?).map_err(|_| {
805 UWireMetadataError::MalformedMetadata("length prefix does not fit usize".to_string())
806 })?;
807 self.take(len)
808 }
809}
810
811#[macro_export]
872macro_rules! bind_wire_self_codec {
873 ($wire:ty : $($payload:ty),+ $(,)?) => {
874 $(
875 impl $crate::UWirePayload<$payload> for $wire {
876 type Codec = $wire;
877 }
878 )+
879 };
880}
881
882#[cfg(test)]
883mod tests {
884 #[cfg(feature = "protobuf-support")]
885 use protobuf::well_known_types::wrappers::StringValue;
886
887 use super::*;
888 use crate::payload::{
889 codec::PayloadCodec,
890 loan::{BorrowPayload, LoanPayload},
891 };
892 use crate::test_support::StableTestBytes as WireStableBytes;
893
894 fn assert_wire_payload<T, W>()
895 where
896 W: UWirePayload<T>,
897 <W as UWirePayload<T>>::Codec: LoanPayload<T> + BorrowPayload<T> + PayloadCodec,
898 {
899 }
900
901 #[cfg(feature = "zero-copy-transport")]
902 fn assert_wire_uninit_payload<T, W>()
903 where
904 W: UWirePayload<T>,
905 <W as UWirePayload<T>>::Codec: crate::payload::loan::LoanUninitPayload<T>,
906 {
907 }
908
909 #[test]
910 fn first_wave_wire_identity_constants_match_register() {
911 assert_eq!(
912 PROTOBUF_WIRE_ID.literal_id(),
913 "org.eclipse.uprotocol.wire.protobuf"
914 );
915 assert_eq!(PROTOBUF_WIRE_ID.compact_id(), 0x0002);
916 assert_eq!(PROTOBUF_PAYLOAD_FAMILY_ID.literal_id(), "protobuf");
917 assert_eq!(PROTOBUF_PAYLOAD_FAMILY_ID.compact_id(), 0x0002);
918 assert_eq!(
919 XCDR_V2_WIRE_ID.literal_id(),
920 "org.eclipse.uprotocol.wire.xcdr-v2"
921 );
922 assert_eq!(XCDR_V2_WIRE_ID.compact_id(), 0x0003);
923 assert_eq!(XCDR_V2_PAYLOAD_FAMILY_ID.literal_id(), "xcdr-v2");
924 assert_eq!(XCDR_V2_PAYLOAD_FAMILY_ID.compact_id(), 0x0003);
925 assert_eq!(
926 STABLE_CONTAINER_WIRE_ID.literal_id(),
927 "org.eclipse.uprotocol.wire.stable-container"
928 );
929 assert_eq!(STABLE_CONTAINER_WIRE_ID.compact_id(), 0x0004);
930 assert_eq!(
931 STABLE_CONTAINER_PAYLOAD_FAMILY_ID.literal_id(),
932 "stable-container"
933 );
934 assert_eq!(STABLE_CONTAINER_PAYLOAD_FAMILY_ID.compact_id(), 0x0004);
935 }
936
937 #[cfg(feature = "protobuf-support")]
938 #[test]
939 fn protobuf_wire_delegates_application_payload_codec() {
940 let value = StringValue {
941 value: "wire".to_string(),
942 special_fields: Default::default(),
943 };
944
945 let encoded = ProtobufWire::encode_payload_owned(&value).expect("encode protobuf wire");
946 let decoded: StringValue =
947 ProtobufWire::decode_payload(&encoded).expect("decode protobuf wire");
948 assert_eq!(decoded.value, "wire");
949 assert_eq!(
950 ProtobufWire::payload_encoding(),
951 ProtobufPayload::payload_encoding()
952 );
953 }
954
955 #[test]
956 fn stable_container_wire_maps_type_to_capable_codec() {
957 assert_wire_payload::<WireStableBytes, StableContainerWireFormat>();
958 #[cfg(feature = "zero-copy-transport")]
959 assert_wire_uninit_payload::<WireStableBytes, StableContainerWireFormat>();
960
961 let encoding =
962 <StableContainerWireFormat as UWirePayload<WireStableBytes>>::Codec::payload_encoding();
963 let expected = StableContainerPayload::<WireStableBytes>::payload_encoding();
964 assert_eq!(encoding, expected);
965 }
966}