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
82 .render(message)
83 .map_err(|_| coap_message_utils::Error::internal_server_error())?,
84 BuildResponseError::Own(e) => e
85 .render(message)
86 .map_err(|_| coap_message_utils::Error::internal_server_error())?,
87 }
88 Ok(())
89 }
90}
91
92use RequestedScrollbufPosition::*;
93
94impl<'a, const N: usize> coap_handler::Handler for BufferHandler<'a, N> {
95 type RequestData = RequestedScrollbufPosition;
96
97 type ExtractRequestError = Error;
98 type BuildResponseError<M: MinimalWritableMessage> = BuildResponseError<M::UnionError>;
99
100 fn extract_request_data<M: ReadableMessage>(
101 &mut self,
102 req: &M,
103 ) -> Result<RequestedScrollbufPosition, Error> {
104 use coap_message::MessageOption;
105 use coap_message_utils::OptionsExt;
106
107 req.options()
108 .filter(|o| {
109 !(o.number() == option::ACCEPT
110 && o.value_uint::<u16>()
111 == Some(content_format::from_str("application/cbor-seq").unwrap()))
112 })
113 .ignore_elective_others()?;
115
116 Ok(match req.code().into() {
117 code::GET => FromStart,
118 code::FETCH => StartingAt(Wrapping({
119 let payload = req.payload();
120 let (start, bytes_consumed) = match payload.get(0) {
122 Some(i @ 0..=23) => (Some((*i).into()), 1),
123 Some(24) => (payload.get(1).map(|&n| n.into()), 2),
124 Some(25) => (
125 payload
126 .get(1..3)
127 .map(|b| u16::from_be_bytes(b.try_into().unwrap()))
128 .map(u32::from),
129 3,
130 ),
131 Some(26) => (
132 payload
133 .get(1..5)
134 .map(|b| u32::from_be_bytes(b.try_into().unwrap())),
135 5,
136 ),
137 Some(_) => return Err(Error::bad_request_with_rbep(0)),
138 None => return Err(Error::bad_request()),
139 };
140 let Some(start) = start else {
141 return Err(Error::bad_request_with_rbep(payload.len()));
142 };
143 if bytes_consumed != payload.len() {
144 return Err(Error::bad_request_with_rbep(bytes_consumed));
145 }
146 start
147 })),
148 _ => return Err(Error::method_not_allowed()),
149 })
150 }
151
152 fn estimate_length(&mut self, _: &<Self as coap_handler::Handler>::RequestData) -> usize {
153 1100
154 }
155 fn build_response<M: MutableWritableMessage>(
156 &mut self,
157 res: &mut M,
158 mode: RequestedScrollbufPosition,
159 ) -> Result<(), Self::BuildResponseError<M>> {
160 res.set_code(M::Code::new(code::CONTENT).map_err(|e| BuildResponseError::Stack(e.into()))?);
161
162 res.add_option_uint(
163 M::OptionNumber::new(option::CONTENT_FORMAT)
164 .map_err(|e| BuildResponseError::Stack(e.into()))?,
165 content_format::from_str("application/cbor-seq").unwrap(),
166 )
167 .map_err(|e| BuildResponseError::Stack(e.into()))?;
168
169 let len = res.available_space() - 1;
170 let msg_buf = res
171 .payload_mut_with_len(len)
172 .map_err(|e| BuildResponseError::Stack(e.into()))?;
173
174 assert!(len > 8);
175 msg_buf[0] = 0x1a; msg_buf[5] = 0x59; let data_area = &mut msg_buf[8..];
179
180 let (cursor, bytes) = match mode {
181 FromStart => match self.0.read_earliest(data_area) {
182 Ok((cursor, bytes)) => (cursor, bytes),
183 _ => {
184 return Err(BuildResponseError::Own(
187 Error::service_unavailable().with_max_age(0),
188 ));
189 }
190 },
191 StartingAt(n) => match self.0.read_from_cursor(n, data_area) {
192 Ok(bytes) => (n, bytes),
193 Err(scroll_ring::ReadErr::BufferUnavailable) => {
194 return Err(BuildResponseError::Own(
195 Error::service_unavailable().with_max_age(0),
196 ));
197 }
198 Err(scroll_ring::ReadErr::DataUnavailable) => {
199 return Err(BuildResponseError::Own(Error::bad_request()));
202 }
203 },
204 };
205
206 let bytes_written = core::cmp::min(bytes, data_area.len());
207 msg_buf[1..5].copy_from_slice(&cursor.0.to_be_bytes());
208 msg_buf[6..8].copy_from_slice(&(bytes_written as u16).to_be_bytes());
209 res.truncate(8 + bytes_written)
210 .map_err(|e| BuildResponseError::Stack(e.into()))?;
211
212 Ok(())
213 }
214}
215
216#[doc(hidden)]
217pub struct BufferHandlerRecord(());
218
219impl<'a, const N: usize> coap_handler::Reporting for BufferHandler<'a, N> {
220 type Record<'b> = BufferHandlerRecord where Self: 'b;
221 type Reporter<'b> = core::iter::Once<BufferHandlerRecord> where Self: 'b;
222 fn report(&self) -> Self::Reporter<'_> {
223 core::iter::once(BufferHandlerRecord(()))
224 }
225}
226
227impl<'a> coap_handler::Record for BufferHandlerRecord {
228 type PathElement = &'static &'static str;
229 type PathElements = core::iter::Empty<&'static &'static str>;
230 type Attributes = core::iter::Once<coap_handler::Attribute>;
231
232 fn path(&self) -> Self::PathElements {
233 core::iter::empty()
234 }
235 fn rel(&self) -> Option<&str> {
236 None
237 }
238 fn attributes(&self) -> Self::Attributes {
239 core::iter::once(coap_handler::Attribute::Interface(
240 "tag:riot-os.org,2021:ser-out",
241 ))
242 }
243}