coap_handler/wkc.rs
1//! Traits to implement a .well-known/core resource easily
2//!
3//! This tries to be future-proof for building also CoRAL responses, without going out of its way
4//! for that.
5
6/// A property an advertised resource can have many of.
7///
8/// This corresponds to target attributes in Link Format, and also to properties in CoRAL without
9/// being very final yet.
10///
11/// This is a single type with static string out-references, but likely to be generalized later
12/// into a trait (but right now it's insufficiently known what it'll need to produce).
13#[non_exhaustive]
14#[derive(Copy, Clone)]
15pub enum Attribute {
16 Observable,
17 Interface(&'static str),
18 ResourceType(&'static str),
19 Title(&'static str),
20 Ct(u16), // Single only -- array could be added in an own option
21 Sz(usize),
22 /// An arbitrary value rendered verbatim into an Link Format document (between `;` or `,`
23 /// depending on its position).
24 ///
25 /// It is preferred to emit attributes explicitly: This both supports any non-RFC6690 outputs
26 /// that can work from that more easily, and dynamic data.
27 Other(&'static str),
28}
29
30impl Attribute {
31 /// Serializes the attribute into RFC6690 Link Format.
32 ///
33 /// Any attribute that can be serialized (currently and expectedly: all) produces a single
34 /// semicolon (`;`) followed by some key and possibly an equals sign (`=`) and a (possibly
35 /// quoted) value.
36 pub fn write_link_format(&self, w: &mut impl core::fmt::Write) -> core::fmt::Result {
37 match self {
38 Attribute::Observable => write!(w, ";obs")?,
39 Attribute::ResourceType(s) => write!(w, ";rt=\"{s}\"")?,
40 Attribute::Interface(s) => write!(w, ";if=\"{s}\"")?,
41 Attribute::Title(s) => write!(w, ";title=\"{s}\"")?,
42 Attribute::Ct(s) => write!(w, ";ct={s}")?,
43 Attribute::Sz(s) => write!(w, ";sz={s}")?,
44 Attribute::Other(s) => write!(w, ";{s}")?,
45 }
46 Ok(())
47 }
48}
49
50/// A entry produced by Reporting, corresponding to a single link in a Link Format file.
51pub trait Record {
52 type PathElement: AsRef<str>;
53 type PathElements: Iterator<Item = Self::PathElement>;
54 type Attributes: Iterator<Item = Attribute>;
55
56 /// List of path segments (equivalent to Uri-Path option values) leading to the indicated
57 /// resoruce
58 fn path(&self) -> Self::PathElements;
59
60 /// Link relation (or None to default to the implicit "hosts")
61 ///
62 /// Note that the allowed character set is limited compared to full UTF-8 strings.
63 fn rel(&self) -> Option<&str>;
64
65 /// Target attributes of the link
66 fn attributes(&self) -> Self::Attributes;
67}
68
69/// Indicates that this resource can produce output for a .well-known/core resource.
70///
71/// Several trivial implementations ([NotReporting] for resources that should not show in
72/// .well-known/core, [ConstantSingleRecordReport] for resources with one static record) are
73/// available that cover most scenarios in which a custom [Handler](crate::Handler) is implemented.
74///
75/// [NotReporting]: https://docs.rs/coap-handler-implementations/0.3.5/coap_handler_implementations/wkc/struct.NotReporting.html
76/// [ConstantSingleRecordReport]: https://docs.rs/coap-handler-implementations/0.3.5/coap_handler_implementations/wkc/struct.ConstantSingleRecordReport.html
77pub trait Reporting {
78 type Record<'res>: Record
79 where
80 Self: 'res;
81 type Reporter<'res>: Iterator<Item = Self::Record<'res>>
82 where
83 Self: 'res;
84
85 fn report(&self) -> Self::Reporter<'_>;
86
87 /// Writes additional lines into an output RFC6690 Link Format document
88 ///
89 /// This output is appended to any records produced.
90 ///
91 /// The `is_first` argument assists both parties in coordination of the `,` delimiter: Unless
92 /// `is_first=true`, this writer needs to prefix its output with a `,` (comma) character, and
93 /// if it produces any output, it sets `*is_first=false`.
94 #[inline]
95 #[allow(unused_variables, reason = "Names are part of user interface")]
96 fn write_extra_link_format(
97 &self,
98 writer: &mut impl core::fmt::Write,
99 is_first: &mut bool,
100 ) -> core::fmt::Result {
101 Ok(())
102 }
103}