coap_scroll_ring_server/
lib.rs1#![no_std]
20
21use coap_message::{
22 Code as _, MinimalWritableMessage, MutableWritableMessage, OptionNumber as _, ReadableMessage,
23};
24use coap_message_utils::Error;
25use coap_numbers::{code, content_format, option};
26use core::num::Wrapping;
27
28pub struct BufferHandler<'a, const N: usize>(&'a scroll_ring::Buffer<N>);
43
44impl<'a, const N: usize> BufferHandler<'a, N> {
45 pub fn new(buffer: &'a scroll_ring::Buffer<N>) -> Self {
51 Self(buffer)
52 }
53}
54
55#[doc(hidden)]
56pub enum RequestedScrollbufPosition {
57 FromStart,
58 StartingAt(Wrapping<u32>),
59}
60
61#[doc(hidden)]
62#[derive(Debug)]
63pub enum BuildResponseError<SE: coap_message::error::RenderableOnMinimal + core::fmt::Debug> {
65 Own(Error),
66 Stack(SE),
67}
68
69impl<SE: coap_message::error::RenderableOnMinimal + core::fmt::Debug>
70 coap_message::error::RenderableOnMinimal for BuildResponseError<SE>
71{
72 type Error<IE: coap_message::error::RenderableOnMinimal + core::fmt::Debug> = Error;
76 fn render<M: MinimalWritableMessage>(
77 self,
78 message: &mut M,
79 ) -> Result<(), Self::Error<M::UnionError>> {
80 match self {
81 BuildResponseError::Stack(se) => se.render(message).map_err(|_| {
82 coap_message_utils::Error::internal_server_error()
83 .with_title("Stack error rendering failed")
84 })?,
85 BuildResponseError::Own(e) => e.render(message).map_err(|_| {
86 coap_message_utils::Error::internal_server_error()
87 .with_title("Own error rendering failed")
88 })?,
89 }
90 Ok(())
91 }
92}
93
94use RequestedScrollbufPosition::*;
95
96impl<'a, const N: usize> coap_handler::Handler for BufferHandler<'a, N> {
97 type RequestData = RequestedScrollbufPosition;
98
99 type ExtractRequestError = Error;
100 type BuildResponseError<M: MinimalWritableMessage> = BuildResponseError<M::UnionError>;
101
102 fn extract_request_data<M: ReadableMessage>(
103 &mut self,
104 req: &M,
105 ) -> Result<RequestedScrollbufPosition, Error> {
106 use coap_message::MessageOption;
107 use coap_message_utils::OptionsExt;
108
109 req.options()
110 .filter(|o| {
111 !(o.number() == option::ACCEPT
112 && o.value_uint::<u16>()
113 == Some(content_format::from_str("application/cbor-seq").unwrap()))
114 })
115 .ignore_elective_others()?;
117
118 Ok(match req.code().into() {
119 code::GET => FromStart,
120 code::FETCH => StartingAt(Wrapping({
121 let payload = req.payload();
122 let (start, bytes_consumed) = match payload.get(0) {
124 Some(i @ 0..=23) => (Some((*i).into()), 1),
125 Some(24) => (payload.get(1).map(|&n| n.into()), 2),
126 Some(25) => (
127 payload
128 .get(1..3)
129 .map(|b| u16::from_be_bytes(b.try_into().unwrap()))
130 .map(u32::from),
131 3,
132 ),
133 Some(26) => (
134 payload
135 .get(1..5)
136 .map(|b| u32::from_be_bytes(b.try_into().unwrap())),
137 5,
138 ),
139 Some(_) => {
140 return Err(
141 Error::bad_request_with_rbep(0).with_title("Expected cursor position")
142 )
143 }
144 None => return Err(Error::bad_request().with_title("Expected some CBOR")),
145 };
146 let Some(start) = start else {
147 return Err(Error::bad_request_with_rbep(payload.len())
148 .with_title("Integer out of range"));
149 };
150 if bytes_consumed != payload.len() {
151 return Err(Error::bad_request_with_rbep(bytes_consumed)
152 .with_title("Data after CBOR item"));
153 }
154 start
155 })),
156 _ => return Err(Error::method_not_allowed()),
157 })
158 }
159
160 fn estimate_length(&mut self, _: &<Self as coap_handler::Handler>::RequestData) -> usize {
161 1100
162 }
163 fn build_response<M: MutableWritableMessage>(
164 &mut self,
165 res: &mut M,
166 mode: RequestedScrollbufPosition,
167 ) -> Result<(), Self::BuildResponseError<M>> {
168 res.set_code(M::Code::new(code::CONTENT).map_err(|e| BuildResponseError::Stack(e.into()))?);
169
170 res.add_option_uint(
171 M::OptionNumber::new(option::CONTENT_FORMAT)
172 .map_err(|e| BuildResponseError::Stack(e.into()))?,
173 content_format::from_str("application/cbor-seq").unwrap(),
174 )
175 .map_err(|e| BuildResponseError::Stack(e.into()))?;
176
177 let len = core::cmp::min(res.available_space() - 1, N + 8);
181 let msg_buf = res
182 .payload_mut_with_len(len)
183 .map_err(|e| BuildResponseError::Stack(e.into()))?;
184
185 assert!(len > 8);
186 msg_buf[0] = 0x1a; msg_buf[5] = 0x59; let data_area = &mut msg_buf[8..];
190
191 let (cursor, bytes) = match mode {
192 FromStart => match self.0.read_earliest(data_area) {
193 Ok((cursor, bytes)) => (cursor, bytes),
194 _ => {
195 return Err(BuildResponseError::Own(
198 Error::service_unavailable()
199 .with_max_age(0)
200 .with_title("Buffer busy"),
201 ));
202 }
203 },
204 StartingAt(n) => match self.0.read_from_cursor(n, data_area) {
205 Ok(bytes) => (n, bytes),
206 Err(scroll_ring::ReadErr::BufferUnavailable) => {
207 return Err(BuildResponseError::Own(
208 Error::service_unavailable()
209 .with_max_age(0)
210 .with_title("Buffer busy"),
211 ));
212 }
213 Err(scroll_ring::ReadErr::DataUnavailable) => {
214 return Err(BuildResponseError::Own(
217 Error::bad_request().with_title("Data unavailable"),
218 ));
219 }
220 },
221 };
222
223 let bytes_written = core::cmp::min(bytes, data_area.len());
224 msg_buf[1..5].copy_from_slice(&cursor.0.to_be_bytes());
225 msg_buf[6..8].copy_from_slice(&(bytes_written as u16).to_be_bytes());
226 res.truncate(8 + bytes_written)
227 .map_err(|e| BuildResponseError::Stack(e.into()))?;
228
229 Ok(())
230 }
231}
232
233#[doc(hidden)]
234pub struct BufferHandlerRecord(());
235
236impl<'a, const N: usize> coap_handler::Reporting for BufferHandler<'a, N> {
237 type Record<'b>
238 = BufferHandlerRecord
239 where
240 Self: 'b;
241 type Reporter<'b>
242 = core::iter::Once<BufferHandlerRecord>
243 where
244 Self: 'b;
245 fn report(&self) -> Self::Reporter<'_> {
246 core::iter::once(BufferHandlerRecord(()))
247 }
248}
249
250impl<'a> coap_handler::Record for BufferHandlerRecord {
251 type PathElement = &'static &'static str;
252 type PathElements = core::iter::Empty<&'static &'static str>;
253 type Attributes = core::iter::Once<coap_handler::Attribute>;
254
255 fn path(&self) -> Self::PathElements {
256 core::iter::empty()
257 }
258 fn rel(&self) -> Option<&str> {
259 None
260 }
261 fn attributes(&self) -> Self::Attributes {
262 core::iter::once(coap_handler::Attribute::Interface(
263 "tag:riot-os.org,2021:ser-out",
264 ))
265 }
266}