use coap_handler::Handler;
use coap_message::{MutableWritableMessage, ReadableMessage};
#[non_exhaustive]
#[derive(Copy, Clone)]
pub enum Attribute {
Observable,
Interface(&'static str),
ResourceType(&'static str),
Title(&'static str),
Ct(u16), Sz(usize),
}
pub trait Record {
type PathElement: AsRef<str>;
type PathElements: Iterator<Item = Self::PathElement>;
type Attributes: Iterator<Item = Attribute>;
fn path(&self) -> Self::PathElements;
fn rel(&self) -> Option<&str>;
fn attributes(&self) -> Self::Attributes;
}
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<'_>;
}
pub struct NotReporting<H: Handler>(H);
impl<H: Handler> NotReporting<H> {
pub fn new(handler: H) -> Self {
NotReporting(handler)
}
}
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)
}
}
pub struct EmptyRecord;
impl Record for EmptyRecord {
type PathElement = &'static &'static str; 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()
}
}
pub struct ConstantSingleRecordReport<'a, H: Handler> {
handler: H,
path: &'a [&'a str],
attributes: &'a [Attribute],
}
impl<'a, H: Handler> ConstantSingleRecordReport<'a, H> {
pub fn new(handler: H, attributes: &'a [Attribute]) -> Self {
ConstantSingleRecordReport {
handler,
path: &[],
attributes,
}
}
pub fn new_with_path(handler: H, attributes: &'a [Attribute], path: &'a [&'a str]) -> Self {
ConstantSingleRecordReport {
handler,
path,
attributes,
}
}
}
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,
})
}
}
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(())
}