coap_handler_implementations/
simple_rendered.rs1use crate::helpers::block2_write_with_cf;
4use crate::{Error, wkc};
5use coap_handler::{Handler, Reporting};
6use coap_message::{
7 Code as _, MessageOption, MinimalWritableMessage, MutableWritableMessage, ReadableMessage,
8};
9use coap_message_utils::option_value::Block2RequestData;
10use coap_numbers::code::{CONTENT, GET};
11use coap_numbers::option::{ACCEPT, BLOCK2, Criticality, get_criticality};
12
13pub struct SimpleRenderableData(Block2RequestData);
16
17pub trait SimpleRenderable {
23 fn render<W: embedded_io::blocking::Write + core::fmt::Write>(&mut self, writer: &mut W);
24
25 fn content_format(&self) -> Option<u16> {
28 None
29 }
30}
31
32#[derive(Debug, Copy, Clone)]
37pub struct SimpleRendered<T: SimpleRenderable>(pub T);
38
39impl<'a> SimpleRendered<TypedStaticRenderable<'a>> {
40 pub fn new_typed_slice(data: &'a [u8], content_format: Option<u16>) -> Self {
41 SimpleRendered(TypedStaticRenderable {
42 data,
43 content_format,
44 })
45 }
46
47 pub fn new_typed_str(data: &'a str, content_format: Option<u16>) -> Self {
48 let data = data.as_bytes();
49 Self::new_typed_slice(data, content_format)
50 }
51}
52
53impl<T> Handler for SimpleRendered<T>
54where
55 T: SimpleRenderable,
56{
57 type RequestData = SimpleRenderableData;
58 type ExtractRequestError = Error;
59 type BuildResponseError<M: MinimalWritableMessage> = M::UnionError;
60
61 fn extract_request_data<M: ReadableMessage>(
62 &mut self,
63 request: &M,
64 ) -> Result<Self::RequestData, Error> {
65 let expected_accept = self.0.content_format();
66
67 let mut block2 = None;
68
69 for o in request.options() {
70 match o.number() {
71 ACCEPT => {
72 if expected_accept.is_some() && o.value_uint() != expected_accept {
73 return Err(Error::bad_option(ACCEPT));
74 }
75 }
76 BLOCK2 => {
77 block2 = match block2 {
78 Some(_) => return Err(Error::bad_request()),
79 None => Block2RequestData::from_option(&o)
80 .map(Some)
81 .map_err(|_| Error::bad_option(BLOCK2))?,
83 }
84 }
85 o if get_criticality(o) == Criticality::Critical => {
86 return Err(Error::bad_option(o));
87 }
88 _ => (),
89 }
90 }
91
92 let reqdata = match request.code().into() {
93 GET => block2.unwrap_or_default(),
94 _ => return Err(Error::method_not_allowed()),
95 };
96 Ok(SimpleRenderableData(reqdata))
97 }
98
99 fn estimate_length(&mut self, _request: &Self::RequestData) -> usize {
100 1280 - 40 - 4 }
102
103 fn build_response<M: MutableWritableMessage>(
104 &mut self,
105 response: &mut M,
106 request: Self::RequestData,
107 ) -> Result<(), Self::BuildResponseError<M>> {
108 let cf = self.0.content_format();
109 let block2data = request.0;
110 response.set_code(M::Code::new(CONTENT)?);
111 block2_write_with_cf(block2data, response, |w| self.0.render(w), cf);
112
113 Ok(())
114 }
115}
116
117impl<T> Reporting for SimpleRendered<T>
118where
119 T: SimpleRenderable,
120{
121 type Record<'a>
122 = wkc::EmptyRecord
123 where
124 Self: 'a;
125 type Reporter<'a>
126 = core::iter::Once<wkc::EmptyRecord>
127 where
128 Self: 'a;
129
130 fn report(&self) -> Self::Reporter<'_> {
131 core::iter::once(wkc::EmptyRecord {})
134 }
135}
136
137impl SimpleRenderable for &str {
138 fn render<W>(&mut self, writer: &mut W)
139 where
140 W: core::fmt::Write,
141 {
142 writer
143 .write_str(self)
144 .expect("The backend of SimpleRenderable supports infallible writing");
145 }
146
147 fn content_format(&self) -> Option<u16> {
148 coap_numbers::content_format::from_str("text/plain; charset=utf-8")
149 }
150}
151
152pub struct TypedStaticRenderable<'a> {
153 data: &'a [u8],
154 content_format: Option<u16>,
155}
156
157impl SimpleRenderable for TypedStaticRenderable<'_> {
158 fn render<W: embedded_io::blocking::Write + core::fmt::Write>(&mut self, writer: &mut W) {
159 writer.write_all(self.data).unwrap();
160 }
161
162 fn content_format(&self) -> Option<u16> {
163 self.content_format
164 }
165}