1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! Traits and tools to implement a .well-known/core resource easily
//!
//! This tries to be future-proof for building also CoRAL responses, without going out of its way
//! for that.
//!
//! The [Reporting] trait is a bit of mis-fit for coap_handler_implementations, and should not be
//! too tightly coupled with the remainign components. It is a candidate for moving in with
//! coap-handler or a to-be created coap-discovery crate (when it and its components have matured),
//! while NotReporting etc. would stay here.

use coap_handler::Handler;
use coap_message::{MutableWritableMessage, ReadableMessage};

/// A property an advertised resource can have many of.
///
/// This corresponds to target attributes in Link Format, and also to properties in CoRAL without
/// being very final yet.
///
/// This is a single type with static string out-references, but likely to be generalized later
/// into a trait (but right now it's insufficiently known what it'll need to produce).
#[non_exhaustive]
#[derive(Copy, Clone)]
pub enum Attribute {
    Observable,
    Interface(&'static str),
    ResourceType(&'static str),
    Title(&'static str),
    Ct(u16), // Single only -- array could be added in an own option
    Sz(usize),
}

/// A entry produced by Reporting, corresponding to a single link in a Link Format file.
pub trait Record {
    type PathElement: AsRef<str>;
    type PathElements: Iterator<Item = Self::PathElement>;
    type Attributes: Iterator<Item = Attribute>;

    /// List of path segments (equivalent to Uri-Path option values) leading to the indicated
    /// resoruce
    fn path(&self) -> Self::PathElements;

    /// Link relation (or None to default to the implicit "hosts")
    ///
    /// Note that the allowed character set is limited compared to full UTF-8 strings.
    fn rel(&self) -> Option<&str>;

    /// Target attributes of the link
    fn attributes(&self) -> Self::Attributes;
}

/// Indicates that this resource can produce output for a .well-known/core resource.
pub trait Reporting {
    type Record<'res>: Record
    where
        Self: 'res;
    type Reporter<'res>: Iterator<Item = Self::Record<'res>>
    where
        Self: 'res;

    fn report(&self) -> Self::Reporter<'_>;
}

/// Wrapper around arbitrary Handlers to make them not report in .well-known/core.
///
/// This helps integrating handler implementations that do not yet also implement Reporting by
/// providing a blank report.
///
/// (With specialization, this could be moved into the default implementation).
pub struct NotReporting<H: Handler>(H);

impl<H: Handler> NotReporting<H> {
    pub fn new(handler: H) -> Self {
        NotReporting(handler)
    }
}

// Is DerefMut appropriate? Would be less code...
impl<H: Handler> Handler for NotReporting<H> {
    type RequestData = H::RequestData;
    fn extract_request_data(&mut self, m: &impl ReadableMessage) -> H::RequestData {
        self.0.extract_request_data(m)
    }
    fn estimate_length(&mut self, r: &Self::RequestData) -> usize {
        self.0.estimate_length(r)
    }
    fn build_response(&mut self, m: &mut impl MutableWritableMessage, r: Self::RequestData) {
        self.0.build_response(m, r)
    }
}

/// A report with no path, relation or attributes
///
/// This is convenient both in places where a type is needed as an element for an Empty iterator
/// (as in NotReporting) and to wrap a resource with no further properties in (in a Once iterator,
/// relying on the tree builder to augment the empty path with the actual path).
pub struct EmptyRecord;

impl Record for EmptyRecord {
    type PathElement = &'static &'static str; // could just as well be !
    type PathElements = core::iter::Empty<&'static &'static str>;
    type Attributes = core::iter::Empty<Attribute>;

    fn path(&self) -> Self::PathElements {
        core::iter::empty()
    }
    fn rel(&self) -> Option<&str> {
        None
    }
    fn attributes(&self) -> Self::Attributes {
        core::iter::empty()
    }
}

impl<H: Handler> Reporting for NotReporting<H> {
    type Record<'a> = EmptyRecord
    where
        Self: 'a,
    ;
    type Reporter<'a> = core::iter::Empty<EmptyRecord>
    where
        Self: 'a,
    ;

    fn report(&self) -> Self::Reporter<'_> {
        core::iter::empty()
    }
}

/// Wrapper around arbitrary Handlers to make them report some attributes. The path is often
/// configured empty, relying on the tree builder to augment it with the actual path.
///
/// This helps integrating handler implementations that do not manually implement Reporting.
///
/// Any Reporting the handler might implement is overridden by this.
pub struct ConstantSingleRecordReport<'a, H: Handler> {
    handler: H,
    path: &'a [&'a str],
    attributes: &'a [Attribute],
}

impl<'a, H: Handler> ConstantSingleRecordReport<'a, H> {
    /// Wrap a handler with attributes reported at its single path
    pub fn new(handler: H, attributes: &'a [Attribute]) -> Self {
        ConstantSingleRecordReport {
            handler,
            path: &[],
            attributes,
        }
    }

    /// Wrap a handler with attributes and additional path components
    ///
    /// Compared to [`.new()`](ConstantSingleRecordReport::new), this is primarily useful for
    /// handlers that *do* perform own path processing (and are thus instanciated `.below()` some
    /// path), but whose root resource is not the resource below which is put. This is a typical
    /// case: To use relative references, it is convenient to make a handler go
    /// `.below(&["path"])`, but then expect an extra `/` segment (an empty component in the path
    /// list) at the end -- and then deal out relative references like `1` or `./2` to go to
    /// `/path/2`. Such handlers put `&[""]` in the path argument.
    pub fn new_with_path(handler: H, attributes: &'a [Attribute], path: &'a [&'a str]) -> Self {
        ConstantSingleRecordReport {
            handler,
            path,
            attributes,
        }
    }
}

// Is DerefMut appropriate? Would be less code...
impl<'a, H: Handler> Handler for ConstantSingleRecordReport<'a, H> {
    type RequestData = H::RequestData;
    fn extract_request_data(&mut self, m: &impl ReadableMessage) -> H::RequestData {
        self.handler.extract_request_data(m)
    }
    fn estimate_length(&mut self, r: &Self::RequestData) -> usize {
        self.handler.estimate_length(r)
    }
    fn build_response(&mut self, m: &mut impl MutableWritableMessage, r: Self::RequestData) {
        self.handler.build_response(m, r)
    }
}

pub struct ConstantSliceRecord<'a> {
    path: &'a [&'a str],
    attributes: &'a [Attribute],
}

impl<'a> Record for ConstantSliceRecord<'a> {
    type PathElement = &'a &'a str;
    type PathElements = core::slice::Iter<'a, &'a str>;
    type Attributes = core::iter::Cloned<core::slice::Iter<'a, Attribute>>;

    fn path(&self) -> Self::PathElements {
        self.path.iter()
    }
    fn rel(&self) -> Option<&str> {
        None
    }
    fn attributes(&self) -> Self::Attributes {
        self.attributes.iter().cloned()
    }
}

impl<'a, H: Handler> Reporting for ConstantSingleRecordReport<'a, H> {
    type Record<'b> = ConstantSliceRecord<'b>
    where
        Self: 'b,
    ;
    type Reporter<'b> = core::iter::Once<ConstantSliceRecord<'b>>
    where
        Self: 'b,
    ;

    fn report(&self) -> Self::Reporter<'_> {
        core::iter::once(ConstantSliceRecord {
            path: self.path,
            attributes: self.attributes,
        })
    }
}

/// Write a Report into a text write in application/link-format.
///
/// As the reports that applications typically get their hands on (as they implement it themselves)
/// don't have the components to make them into an absolute path, but link-format requires using
/// absolute paths, this takes a prefix argument into which the caller has to feed information
/// about where on the CoAP server the resources are actually located.
///
/// Resources that don't ask that information at construction time (it is gone at run time) can not
/// produce meaningful link-format links to their own resources; they could produce output in HTML
/// or CoRAL, but there are currently no Reporting renderers for those.
pub fn write_link_format(
    w: &mut impl core::fmt::Write,
    report: &impl Reporting,
    prefix: &[&str],
) -> core::fmt::Result {
    let mut first = true;
    for record in report.report() {
        if !first {
            write!(w, ",")?;
            first = true;
        }
        write!(w, "<")?;
        for p in prefix.iter() {
            write!(w, "/{}", p)?;
        }
        for p in record.path() {
            write!(w, "/{}", p.as_ref())?;
        }
        write!(w, ">")?;
        if let Some(rel) = record.rel() {
            write!(w, ";rel=\"{}\"", rel)?;
        }
        for attr in record.attributes() {
            match attr {
                Attribute::Observable => write!(w, ";obs")?,
                Attribute::ResourceType(s) => write!(w, ";rt=\"{}\"", s)?,
                Attribute::Interface(s) => write!(w, ";if=\"{}\"", s)?,
                Attribute::Title(s) => write!(w, ";title=\"{}\"", s)?,
                Attribute::Ct(s) => write!(w, ";ct={}", s)?,
                Attribute::Sz(s) => write!(w, ";sz={}", s)?,
            }
        }
    }
    Ok(())
}