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}
94
95impl<'a, H: Handler> ConstantSingleRecordReport<'a, H> {
96    /// Wrap a handler with attributes reported at its single path
97    pub fn new(handler: H, attributes: &'a [Attribute]) -> Self {
98        ConstantSingleRecordReport {
99            handler,
100            path: &[],
101            attributes,
102        }
103    }
104
105    /// Wrap a handler with attributes and additional path components
106    ///
107    /// Compared to [`.new()`](ConstantSingleRecordReport::new), this is primarily useful for
108    /// handlers that *do* perform own path processing (and are thus instanciated `.below()` some
109    /// path), but whose root resource is not the resource below which is put. This is a typical
110    /// case: To use relative references, it is convenient to make a handler go
111    /// `.below(&["path"])`, but then expect an extra `/` segment (an empty component in the path
112    /// list) at the end -- and then deal out relative references like `1` or `./2` to go to
113    /// `/path/2`. Such handlers put `&[""]` in the path argument.
114    pub fn new_with_path(handler: H, attributes: &'a [Attribute], path: &'a [&'a str]) -> Self {
115        ConstantSingleRecordReport {
116            handler,
117            path,
118            attributes,
119        }
120    }
121}
122
123// Is DerefMut appropriate? Would be less code...
124impl<H: Handler> Handler for ConstantSingleRecordReport<'_, H> {
125    type RequestData = H::RequestData;
126    type ExtractRequestError = H::ExtractRequestError;
127    type BuildResponseError<M: MinimalWritableMessage> = H::BuildResponseError<M>;
128    fn extract_request_data<M: ReadableMessage>(
129        &mut self,
130        m: &M,
131    ) -> Result<H::RequestData, Self::ExtractRequestError> {
132        self.handler.extract_request_data(m)
133    }
134    fn estimate_length(&mut self, r: &Self::RequestData) -> usize {
135        self.handler.estimate_length(r)
136    }
137    fn build_response<M: MutableWritableMessage>(
138        &mut self,
139        m: &mut M,
140        r: Self::RequestData,
141    ) -> Result<(), Self::BuildResponseError<M>> {
142        self.handler.build_response(m, r)
143    }
144}
145
146/// A record that serves path and attributes from slices.
147///
148/// This is mostly used internally, but can be used through the public constructors in a
149/// [`Reporting::report()`] method.
150///
151/// This enables relatively simple creation of reports directly at the resource type level
152/// (although unfortunately, doing this is still way more verbose than adding a resource using
153/// [`.at_with_attributes()`][crate::HandlerBuilder::at_with_attributes]).
154pub struct ConstantSliceRecord<'a> {
155    path: &'a [&'a str],
156    attributes: &'a [Attribute],
157}
158
159impl<'a> ConstantSliceRecord<'a> {
160    /// Constructs a record from attributes.
161    ///
162    /// The path is empty, as is suitable for the large class of handlers that do not process any
163    /// Uri-Path options on their own.
164    pub fn new(attributes: &'a [Attribute]) -> Self {
165        Self {
166            path: &[],
167            attributes,
168        }
169    }
170
171    /// Constructs a record from path segments and attributes.
172    pub fn new_with_path(path: &'a [&'a str], attributes: &'a [Attribute]) -> Self {
173        Self { path, attributes }
174    }
175}
176
177impl<'a> Record for ConstantSliceRecord<'a> {
178    type PathElement = &'a &'a str;
179    type PathElements = core::slice::Iter<'a, &'a str>;
180    type Attributes = core::iter::Cloned<core::slice::Iter<'a, Attribute>>;
181
182    fn path(&self) -> Self::PathElements {
183        self.path.iter()
184    }
185    fn rel(&self) -> Option<&str> {
186        None
187    }
188    fn attributes(&self) -> Self::Attributes {
189        self.attributes.iter().cloned()
190    }
191}
192
193impl<H: Handler> Reporting for ConstantSingleRecordReport<'_, H> {
194    type Record<'b>
195        = ConstantSliceRecord<'b>
196    where
197        Self: 'b;
198    type Reporter<'b>
199        = core::iter::Once<ConstantSliceRecord<'b>>
200    where
201        Self: 'b;
202
203    fn report(&self) -> Self::Reporter<'_> {
204        core::iter::once(ConstantSliceRecord {
205            path: self.path,
206            attributes: self.attributes,
207        })
208    }
209}
210
211/// Write a Report into a text write in application/link-format.
212///
213/// As the reports that applications typically get their hands on (as they implement it themselves)
214/// don't have the components to make them into an absolute path, but link-format requires using
215/// absolute paths, this takes a prefix argument into which the caller has to feed information
216/// about where on the CoAP server the resources are actually located.
217///
218/// Resources that don't ask that information at construction time (it is gone at run time) can not
219/// produce meaningful link-format links to their own resources; they could produce output in HTML
220/// or CoRAL, but there are currently no Reporting renderers for those.
221///
222/// ```
223/// use coap_handler_implementations::{wkc::write_link_format, *};
224/// let mut out = String::new();
225/// write_link_format(&mut out,
226///     &new_dispatcher()
227///         .at(&["name"], SimpleRendered::new_typed_str("Demo program", Some(0)))
228///         .at(&["version"], SimpleRendered::new_typed_str("0.8.15", Some(0))),
229///     &["dev"]
230///     ).unwrap();
231/// assert_eq!(out, "</dev/name>,</dev/version>");
232/// ```
233pub fn write_link_format(
234    w: &mut impl core::fmt::Write,
235    report: &impl Reporting,
236    prefix: &[&str],
237) -> core::fmt::Result {
238    let mut first = true;
239    for record in report.report() {
240        if !first {
241            write!(w, ",")?;
242        } else {
243            first = false;
244        }
245        write!(w, "<")?;
246        for p in prefix.iter() {
247            write!(w, "/{p}")?;
248        }
249        for p in record.path() {
250            write!(w, "/{}", p.as_ref())?;
251        }
252        write!(w, ">")?;
253        if let Some(rel) = record.rel() {
254            write!(w, ";rel=\"{rel}\"")?;
255        }
256        for attr in record.attributes() {
257            attr.write_link_format(w)?;
258        }
259    }
260    report.write_extra_link_format(w, &mut first)?;
261    Ok(())
262}