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
146pub struct ConstantSliceRecord<'a> {
147    path: &'a [&'a str],
148    attributes: &'a [Attribute],
149}
150
151impl<'a> Record for ConstantSliceRecord<'a> {
152    type PathElement = &'a &'a str;
153    type PathElements = core::slice::Iter<'a, &'a str>;
154    type Attributes = core::iter::Cloned<core::slice::Iter<'a, Attribute>>;
155
156    fn path(&self) -> Self::PathElements {
157        self.path.iter()
158    }
159    fn rel(&self) -> Option<&str> {
160        None
161    }
162    fn attributes(&self) -> Self::Attributes {
163        self.attributes.iter().cloned()
164    }
165}
166
167impl<H: Handler> Reporting for ConstantSingleRecordReport<'_, H> {
168    type Record<'b>
169        = ConstantSliceRecord<'b>
170    where
171        Self: 'b;
172    type Reporter<'b>
173        = core::iter::Once<ConstantSliceRecord<'b>>
174    where
175        Self: 'b;
176
177    fn report(&self) -> Self::Reporter<'_> {
178        core::iter::once(ConstantSliceRecord {
179            path: self.path,
180            attributes: self.attributes,
181        })
182    }
183}
184
185/// Write a Report into a text write in application/link-format.
186///
187/// As the reports that applications typically get their hands on (as they implement it themselves)
188/// don't have the components to make them into an absolute path, but link-format requires using
189/// absolute paths, this takes a prefix argument into which the caller has to feed information
190/// about where on the CoAP server the resources are actually located.
191///
192/// Resources that don't ask that information at construction time (it is gone at run time) can not
193/// produce meaningful link-format links to their own resources; they could produce output in HTML
194/// or CoRAL, but there are currently no Reporting renderers for those.
195///
196/// ```
197/// use coap_handler_implementations::{wkc::write_link_format, *};
198/// let mut out = String::new();
199/// write_link_format(&mut out,
200///     &new_dispatcher()
201///         .at(&["name"], SimpleRendered::new_typed_str("Demo program", Some(0)))
202///         .at(&["version"], SimpleRendered::new_typed_str("0.8.15", Some(0))),
203///     &["dev"]
204///     ).unwrap();
205/// assert_eq!(out, "</dev/name>,</dev/version>");
206/// ```
207pub fn write_link_format(
208    w: &mut impl core::fmt::Write,
209    report: &impl Reporting,
210    prefix: &[&str],
211) -> core::fmt::Result {
212    let mut first = true;
213    for record in report.report() {
214        if !first {
215            write!(w, ",")?;
216        } else {
217            first = false;
218        }
219        write!(w, "<")?;
220        for p in prefix.iter() {
221            write!(w, "/{p}")?;
222        }
223        for p in record.path() {
224            write!(w, "/{}", p.as_ref())?;
225        }
226        write!(w, ">")?;
227        if let Some(rel) = record.rel() {
228            write!(w, ";rel=\"{rel}\"")?;
229        }
230        for attr in record.attributes() {
231            match attr {
232                Attribute::Observable => write!(w, ";obs")?,
233                Attribute::ResourceType(s) => write!(w, ";rt=\"{s}\"")?,
234                Attribute::Interface(s) => write!(w, ";if=\"{s}\"")?,
235                Attribute::Title(s) => write!(w, ";title=\"{s}\"")?,
236                Attribute::Ct(s) => write!(w, ";ct={s}")?,
237                Attribute::Sz(s) => write!(w, ";sz={s}")?,
238                // New attributes are ignored -- as a matter of best practice, when a new attribute
239                // is added, it should be covered here.
240                _ => (),
241            }
242        }
243    }
244    Ok(())
245}