coap_handler_implementations/
helpers.rs1use coap_message::{MessageOption, MutableWritableMessage, ReadableMessage};
2use coap_message_utils::option_value::Block2RequestData;
3use coap_numbers::option;
4
5use windowed_infinity::WindowedInfinity;
6
7pub(crate) fn optconvert<O: TryFrom<u16>>(option: u16) -> O {
10 option
11 .try_into()
12 .map_err(|_| "Response type can't express options required by handler")
13 .unwrap()
14}
15
16pub fn block2_write<F, R>(
32 block2: Block2RequestData,
33 response: &mut impl MutableWritableMessage,
34 f: F,
35) -> R
36where
37 F: FnOnce(&mut crate::embedded_io::EmbeddedIoOrFmtWrite<'_>) -> R,
38{
39 block2_write_with_cf(block2, response, f, None)
40}
41
42#[derive(PartialEq)]
43enum Characterization {
44 Underflow,
45 Inside,
46 Overflow,
47}
48
49use Characterization::*;
50
51impl Characterization {
52 fn new(cursor: isize, buffer: &[u8]) -> Self {
53 match usize::try_from(cursor) {
54 Err(_) => Underflow,
55 Ok(i) if i == buffer.len() => Inside,
56 _ => Overflow,
57 }
58 }
59}
60
61pub(crate) fn block2_write_with_cf<F, R>(
65 block2: Block2RequestData,
66 response: &mut impl MutableWritableMessage,
67 f: F,
68 cf: Option<u16>,
69) -> R
70where
71 F: FnOnce(&mut crate::embedded_io::EmbeddedIoOrFmtWrite<'_>) -> R,
72{
73 let estimated_option_size = 25; let payload_budget = response.available_space() - estimated_option_size;
75 let block2 = block2
76 .shrink(payload_budget as u16)
77 .expect("Tiny buffer allocated");
78
79 response
80 .add_option(optconvert(option::ETAG), &[0, 0, 0, 0, 0, 0, 0, 0])
81 .unwrap();
82 if let Some(cf) = cf
83 && let Ok(cfopt) = option::CONTENT_FORMAT.try_into()
84 {
85 response.add_option_uint(cfopt, cf).unwrap();
86 }
87 response
88 .add_option_uint(optconvert(option::BLOCK2), block2.to_option_value(false))
89 .unwrap();
90
91 let (characterization, written, etag, ret) = {
92 let full_payload = response.payload_mut_with_len(block2.size().into()).unwrap();
93 let writer = WindowedInfinity::new(
94 &mut full_payload[..block2.size() as usize],
95 -(block2.start() as isize),
96 );
97 let etag = crc::Crc::<u64>::new(&crc::CRC_64_ECMA_182);
98 let etag = etag.digest();
99 let mut writer = crate::embedded_io::EmbeddedIoOrFmtWrite(tee_embedded_io::Tee::new(
100 writer,
101 extra_embedded_io_adapters::WritableCrc(etag),
102 ));
103
104 let ret = f(&mut writer);
105
106 let (writer, extra_embedded_io_adapters::WritableCrc(etag)) = writer.0.into_parts();
107 let written = writer.written();
108
109 (
110 Characterization::new(writer.cursor(), written),
111 written.len(),
112 etag.finalize().to_le_bytes(),
113 ret,
114 )
115 };
116
117 response.truncate(written).unwrap();
118 if characterization == Underflow {
119 unimplemented!("Report out-of-band seek");
120 }
121
122 response.mutate_options(|optnum, value| {
123 match optnum.into() {
124 option::ETAG => {
125 value.copy_from_slice(&etag);
126 }
127 option::BLOCK2 if characterization == Overflow => {
128 value[value.len() - 1] |= 0x08;
130 }
131 _ => (),
132 };
133 });
134
135 ret
136}
137
138pub struct MaskingUriUpToPath<'m, M: ReadableMessage>(pub &'m M);
146
147impl<M: ReadableMessage> ReadableMessage for MaskingUriUpToPath<'_, M> {
148 type Code = M::Code;
149 type MessageOption<'a>
150 = M::MessageOption<'a>
151 where
152 Self: 'a;
153 type OptionsIter<'a>
154 = MaskingUriUpToPathIter<M::OptionsIter<'a>>
155 where
156 Self: 'a;
157
158 fn options(&self) -> Self::OptionsIter<'_> {
159 MaskingUriUpToPathIter(self.0.options())
160 }
161
162 fn code(&self) -> M::Code {
163 self.0.code()
164 }
165
166 fn payload(&self) -> &[u8] {
167 self.0.payload()
168 }
169}
170
171pub struct MaskingUriUpToPathIter<I>(I);
172
173impl<MO: MessageOption, I: Iterator<Item = MO>> Iterator for MaskingUriUpToPathIter<I> {
174 type Item = MO;
175
176 fn next(&mut self) -> Option<MO> {
177 loop {
178 let result = self.0.next()?;
179 match result.number() {
180 coap_numbers::option::URI_HOST => continue,
181 coap_numbers::option::URI_PATH => continue,
182 _ => return Some(result),
183 }
184 }
185 }
186}
187
188pub(crate) struct MaskingUriUpToPathN<'m, M: ReadableMessage> {
191 message: &'m M,
192 strip_paths: usize,
193}
194
195impl<'m, M: ReadableMessage> MaskingUriUpToPathN<'m, M> {
196 pub(crate) fn new(message: &'m M, strip_paths: usize) -> Self {
197 Self {
198 message,
199 strip_paths,
200 }
201 }
202}
203
204impl<M: ReadableMessage> ReadableMessage for MaskingUriUpToPathN<'_, M> {
205 type Code = M::Code;
206 type MessageOption<'a>
207 = M::MessageOption<'a>
208 where
209 Self: 'a;
210 type OptionsIter<'a>
211 = MaskingUriUpToPathNIter<M::OptionsIter<'a>>
212 where
213 Self: 'a;
214
215 fn options(&self) -> Self::OptionsIter<'_> {
216 MaskingUriUpToPathNIter {
217 inner: self.message.options(),
218 remaining_strip: self.strip_paths,
219 }
220 }
221
222 fn code(&self) -> M::Code {
223 self.message.code()
224 }
225
226 fn payload(&self) -> &[u8] {
227 self.message.payload()
228 }
229}
230
231pub struct MaskingUriUpToPathNIter<I> {
232 inner: I,
233 remaining_strip: usize,
234}
235
236impl<MO: MessageOption, I: Iterator<Item = MO>> Iterator for MaskingUriUpToPathNIter<I> {
237 type Item = MO;
238
239 fn next(&mut self) -> Option<MO> {
240 loop {
241 let result = self.inner.next()?;
242 match result.number() {
243 coap_numbers::option::URI_HOST => continue,
244 coap_numbers::option::URI_PATH if self.remaining_strip > 0 => {
245 self.remaining_strip -= 1;
246 continue;
247 }
248 _ => return Some(result),
249 }
250 }
251 }
252}