Skip to main content

coap_handler_implementations/
wkc.rs

1//! Tools to implement a .well-known/core resource easily
2
3use coap_handler::Handler;
4use coap_message::{MinimalWritableMessage, MutableWritableMessage, ReadableMessage};
5
6use coap_handler::{Attribute, Record, Reporting};
7
8/// Wrapper around arbitrary Handlers to make them not report in .well-known/core.
9///
10/// This helps integrating handler implementations that do not yet also implement Reporting by
11/// providing a blank report.
12///
13/// (With specialization, this could be moved into the default implementation).
14pub struct NotReporting<H: Handler>(H);
15
16impl<H: Handler> NotReporting<H> {
17    pub fn new(handler: H) -> Self {
18        NotReporting(handler)
19    }
20}
21
22// Is DerefMut appropriate? Would be less code...
23impl<H: Handler> Handler for NotReporting<H> {
24    type RequestData = H::RequestData;
25    type ExtractRequestError = H::ExtractRequestError;
26    type BuildResponseError<M: MinimalWritableMessage> = H::BuildResponseError<M>;
27    fn extract_request_data<M: ReadableMessage>(
28        &mut self,
29        m: &M,
30    ) -> Result<H::RequestData, Self::ExtractRequestError> {
31        self.0.extract_request_data(m)
32    }
33    fn estimate_length(&mut self, r: &Self::RequestData) -> usize {
34        self.0.estimate_length(r)
35    }
36    fn build_response<M: MutableWritableMessage>(
37        &mut self,
38        m: &mut M,
39        r: Self::RequestData,
40    ) -> Result<(), Self::BuildResponseError<M>> {
41        self.0.build_response(m, r)
42    }
43}
44
45/// A report with no path, relation or attributes
46///
47/// This is convenient both in places where a type is needed as an element for an Empty iterator
48/// (as in NotReporting) and to wrap a resource with no further properties in (in a Once iterator,
49/// relying on the tree builder to augment the empty path with the actual path).
50pub struct EmptyRecord;
51
52impl Record for EmptyRecord {
53    type PathElement = &'static &'static str; // could just as well be !
54    type PathElements = core::iter::Empty<&'static &'static str>;
55    type Attributes = core::iter::Empty<Attribute>;
56
57    fn path(&self) -> Self::PathElements {
58        core::iter::empty()
59    }
60    fn rel(&self) -> Option<&str> {
61        None
62    }
63    fn attributes(&self) -> Self::Attributes {
64        core::iter::empty()
65    }
66}
67
68impl<H: Handler> Reporting for NotReporting<H> {
69    type Record<'a>
70        = EmptyRecord
71    where
72        Self: 'a;
73    type Reporter<'a>
74        = core::iter::Empty<EmptyRecord>
75    where
76        Self: 'a;
77
78    fn report(&self) -> Self::Reporter<'_> {
79        core::iter::empty()
80    }
81}
82
83/// Wrapper around arbitrary Handlers to make them report some attributes. The path is often
84/// configured empty, relying on the tree builder to augment it with the actual path.
85///
86/// This helps integrating handler implementations that do not manually implement Reporting.
87///
88/// Any Reporting the handler might implement is overridden by this.
89pub struct ConstantSingleRecordReport<'a, H: Handler> {
90    handler: H,
91    path: &'a [&'a str],
92    attributes: &'a [Attribute],
93    rel: Option<&'a str>,
94}
95
96impl<'a, H: Handler> ConstantSingleRecordReport<'a, H> {
97    /// Wrap a handler with attributes reported at its single path
98    pub fn new(handler: H, attributes: &'a [Attribute]) -> Self {
99        ConstantSingleRecordReport {
100            handler,
101            path: &[],
102            attributes,
103            rel: None,
104        }
105    }
106
107    /// Wrap a handler with attributes and additional path components
108    ///
109    /// Compared to [`.new()`](ConstantSingleRecordReport::new), this is primarily useful for
110    /// handlers that *do* perform own path processing (and are thus instanciated `.below()` some
111    /// path), but whose root resource is not the resource below which is put. This is a typical
112    /// case: To use relative references, it is convenient to make a handler go
113    /// `.below(&["path"])`, but then expect an extra `/` segment (an empty component in the path
114    /// list) at the end -- and then deal out relative references like `1` or `./2` to go to
115    /// `/path/2`. Such handlers put `&[""]` in the path argument.
116    pub fn new_with_path(handler: H, attributes: &'a [Attribute], path: &'a [&'a str]) -> Self {
117        ConstantSingleRecordReport {
118            handler,
119            path,
120            attributes,
121            rel: None,
122        }
123    }
124
125    /// Adds a link relation (`rel=`) value.
126    pub fn with_rel(self, rel: &'a str) -> Self {
127        Self {
128            rel: Some(rel),
129            ..self
130        }
131    }
132}
133
134// Is DerefMut appropriate? Would be less code...
135impl<H: Handler> Handler for ConstantSingleRecordReport<'_, H> {
136    type RequestData = H::RequestData;
137    type ExtractRequestError = H::ExtractRequestError;
138    type BuildResponseError<M: MinimalWritableMessage> = H::BuildResponseError<M>;
139    fn extract_request_data<M: ReadableMessage>(
140        &mut self,
141        m: &M,
142    ) -> Result<H::RequestData, Self::ExtractRequestError> {
143        self.handler.extract_request_data(m)
144    }
145    fn estimate_length(&mut self, r: &Self::RequestData) -> usize {
146        self.handler.estimate_length(r)
147    }
148    fn build_response<M: MutableWritableMessage>(
149        &mut self,
150        m: &mut M,
151        r: Self::RequestData,
152    ) -> Result<(), Self::BuildResponseError<M>> {
153        self.handler.build_response(m, r)
154    }
155}
156
157/// A record that serves path and attributes from slices.
158///
159/// This is mostly used internally, but can be used through the public constructors in a
160/// [`Reporting::report()`] method.
161///
162/// This enables relatively simple creation of reports directly at the resource type level
163/// (although unfortunately, doing this is still way more verbose than adding a resource using
164/// [`.at_with_attributes()`][crate::HandlerBuilder::at_with_attributes]).
165pub struct ConstantSliceRecord<'a> {
166    path: &'a [&'a str],
167    attributes: &'a [Attribute],
168    rel: Option<&'a str>,
169}
170
171impl<'a> ConstantSliceRecord<'a> {
172    /// Constructs a record from attributes.
173    ///
174    /// The path is empty, as is suitable for the large class of handlers that do not process any
175    /// Uri-Path options on their own.
176    pub fn new(attributes: &'a [Attribute]) -> Self {
177        Self {
178            path: &[],
179            attributes,
180            rel: None,
181        }
182    }
183
184    /// Constructs a record from path segments and attributes.
185    pub fn new_with_path(path: &'a [&'a str], attributes: &'a [Attribute]) -> Self {
186        Self {
187            path,
188            attributes,
189            rel: None,
190        }
191    }
192
193    /// Adds a link relation (`rel=`) value.
194    pub fn with_rel(self, rel: &'a str) -> Self {
195        Self {
196            rel: Some(rel),
197            ..self
198        }
199    }
200}
201
202impl<'a> Record for ConstantSliceRecord<'a> {
203    type PathElement = &'a &'a str;
204    type PathElements = core::slice::Iter<'a, &'a str>;
205    type Attributes = core::iter::Cloned<core::slice::Iter<'a, Attribute>>;
206
207    fn path(&self) -> Self::PathElements {
208        self.path.iter()
209    }
210    fn rel(&self) -> Option<&str> {
211        self.rel
212    }
213    fn attributes(&self) -> Self::Attributes {
214        self.attributes.iter().cloned()
215    }
216}
217
218impl<H: Handler> Reporting for ConstantSingleRecordReport<'_, H> {
219    type Record<'b>
220        = ConstantSliceRecord<'b>
221    where
222        Self: 'b;
223    type Reporter<'b>
224        = core::iter::Once<ConstantSliceRecord<'b>>
225    where
226        Self: 'b;
227
228    fn report(&self) -> Self::Reporter<'_> {
229        core::iter::once(ConstantSliceRecord {
230            path: self.path,
231            attributes: self.attributes,
232            rel: self.rel,
233        })
234    }
235}
236
237/// Write a Report into a text write in application/link-format.
238///
239/// As the reports that applications typically get their hands on (as they implement it themselves)
240/// don't have the components to make them into an absolute path, but link-format requires using
241/// absolute paths, this takes a prefix argument into which the caller has to feed information
242/// about where on the CoAP server the resources are actually located.
243///
244/// Resources that don't ask that information at construction time (it is gone at run time) can not
245/// produce meaningful link-format links to their own resources; they could produce output in HTML
246/// or CoRAL, but there are currently no Reporting renderers for those.
247///
248/// ```
249/// use coap_handler_implementations::{wkc::write_link_format, *};
250/// let mut out = String::new();
251/// write_link_format(&mut out,
252///     &new_dispatcher()
253///         .at(&["name"], SimpleRendered::new_typed_str("Demo program", Some(0)))
254///         .at(&["version"], SimpleRendered::new_typed_str("0.8.15", Some(0))),
255///     &["dev"]
256///     ).unwrap();
257/// assert_eq!(out, "</dev/name>,</dev/version>");
258/// ```
259pub fn write_link_format(
260    w: &mut impl core::fmt::Write,
261    report: &impl Reporting,
262    prefix: &[&str],
263) -> core::fmt::Result {
264    let mut first = true;
265    for record in report.report() {
266        if !first {
267            write!(w, ",")?;
268        } else {
269            first = false;
270        }
271        write!(w, "<")?;
272        for p in prefix.iter() {
273            write!(w, "/{p}")?;
274        }
275        for p in record.path() {
276            write!(w, "/{}", p.as_ref())?;
277        }
278        write!(w, ">")?;
279        if let Some(rel) = record.rel() {
280            write!(w, ";rel=\"{rel}\"")?;
281        }
282        for attr in record.attributes() {
283            attr.write_link_format(w)?;
284        }
285    }
286    report.write_extra_link_format(w, &mut first)?;
287    Ok(())
288}