1#[cfg(feature = "owned-frame-transport")]
23use std::io::Cursor;
24use std::io::Read;
25
26use crate::frame::metadata::{UFrameMetadata, UFrameMetadataError};
27use crate::payload::codec::{PayloadCodec, ReadDecodePayload};
28use crate::payload::UWireError;
29#[cfg(feature = "owned-frame-transport")]
30use crate::UOwnedFrame;
31use crate::{UCode, UStatus};
32
33fn invalid_argument(message: impl Into<String>) -> UStatus {
34 UStatus::fail_with_code(UCode::InvalidArgument, message)
35}
36
37fn internal_view_error(message: impl Into<String>) -> UStatus {
38 UStatus::fail_with_code(UCode::Internal, message)
39}
40
41pub(crate) fn validate_metadata(metadata: &UFrameMetadata) -> Result<(), UStatus> {
42 metadata.validate().map_err(frame_metadata_error)
43}
44
45pub(crate) fn frame_metadata_error(error: UFrameMetadataError) -> UStatus {
46 invalid_argument(format!("invalid frame metadata: {error}"))
47}
48
49pub(crate) fn validate_payload_presence(
50 has_payload: bool,
51 has_encoding: bool,
52 context: &str,
53) -> Result<(), UStatus> {
54 match (has_payload, has_encoding) {
55 (true, true) | (false, false) => Ok(()),
56 (true, false) => Err(invalid_argument(format!(
57 "{context} carries payload bytes without payload encoding"
58 ))),
59 (false, true) => Err(invalid_argument(format!(
60 "{context} carries payload encoding without payload bytes"
61 ))),
62 }
63}
64
65pub trait UFrameView {
69 type PayloadReader<'a>: Read + 'a
71 where
72 Self: 'a;
73 type PayloadSlices<'a>: Iterator<Item = &'a [u8]> + 'a
75 where
76 Self: 'a;
77
78 fn metadata(&self) -> &UFrameMetadata;
80
81 fn payload_len(&self) -> usize;
83
84 fn has_payload(&self) -> bool {
86 self.payload_len() > 0 || self.metadata().payload_encoding().is_some()
87 }
88
89 fn payload_reader(&self) -> Self::PayloadReader<'_>;
91
92 fn payload_slices(&self) -> Self::PayloadSlices<'_>;
94
95 fn try_contiguous_payload(&self) -> Option<&[u8]> {
97 None
98 }
99
100 fn decode_payload_from_reader_as<C, T>(&self) -> Result<T, UWireError>
110 where
111 C: PayloadCodec + ReadDecodePayload<T>,
112 {
113 C::verify_encoding(self.metadata().payload_encoding())?;
114 if !self.has_payload() {
115 return Err(UWireError::MissingPayload);
116 }
117 C::decode_payload_from_reader(self.payload_reader(), self.payload_len())
118 }
119}
120
121#[cfg(feature = "owned-frame-transport")]
122impl UFrameView for UOwnedFrame {
123 type PayloadReader<'a>
124 = Cursor<&'a [u8]>
125 where
126 Self: 'a;
127 type PayloadSlices<'a>
128 = std::option::IntoIter<&'a [u8]>
129 where
130 Self: 'a;
131
132 fn metadata(&self) -> &UFrameMetadata {
133 self.metadata()
134 }
135
136 fn payload_len(&self) -> usize {
137 self.payload_bytes().len()
138 }
139
140 fn has_payload(&self) -> bool {
141 UOwnedFrame::has_payload(self)
142 }
143
144 fn payload_reader(&self) -> Self::PayloadReader<'_> {
145 Cursor::new(self.payload_bytes())
146 }
147
148 fn payload_slices(&self) -> Self::PayloadSlices<'_> {
149 self.payload().map(bytes::Bytes::as_ref).into_iter()
150 }
151
152 fn try_contiguous_payload(&self) -> Option<&[u8]> {
153 self.payload().map(bytes::Bytes::as_ref)
154 }
155}
156
157pub fn validate_frame_view_for_transport(
164 frame: &(impl UFrameView + ?Sized),
165) -> Result<(), UStatus> {
166 validate_metadata(frame.metadata())?;
167 validate_payload_presence(
168 frame.has_payload(),
169 frame.metadata().payload_encoding().is_some(),
170 "frame view",
171 )?;
172 let mut observed = 0_usize;
173 for slice in frame.payload_slices() {
174 observed = observed
175 .checked_add(slice.len())
176 .ok_or_else(|| internal_view_error("frame view payload slices overflow usize"))?;
177 }
178 if observed != frame.payload_len() {
179 return Err(internal_view_error(format!(
180 "frame view payload slices yielded {observed} bytes but payload_len returned {}",
181 frame.payload_len()
182 )));
183 }
184 Ok(())
185}