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
//! The `coap-request` crate defines an interface provided by a CoAP client stack towards
//! applications that can send requests through it.
//!
//! It is the client side equivalent of the [coap-handler] crate.
//!
//! [coap-handler]: https://docs.rs/coap-handler/
//!
//! ## Usability
//!
//! This crate provides a very low-level and generic interface, which is not ideal for every-day
//! "GET me the plain text content of coap://example.com/foo" style requests. The crate
//! [coap-request-implementations] will fill the gap, just as [coap-handler-implementations] does
//! for [coap-handler].
//!
//! [coap-request-implementations]: https://docs.rs/coap-request-implementations/
//! [coap-handler-implementations]: https://docs.rs/coap-handler-implementations/
//!
//! ## Caveats
//!
//! There boundary of responsibilities for respecting protocol level requirements is not clearly
//! established yet. For example, it is unclear how the application learns special option encoding
//! rules it needs to follow given the transport, or whether other operations on the same stack
//! have concluded their operations w/rt Request-Tag processing.
//!
//! It is unclear yet whether the async functions should be Send or not (or whether distinct traits
//! are needed to cater for both cases).
//!
//! The [Request] trait is not suitable for requests for which responses are processed in a pooled
//! fashion (where every response comes in to a single handler to be dissected later); in
//! particular, this trait doesn't lend itself to the implementation of a stateless proxy that uses
//! long encrypted tokens to restore the application state. This may be resolved in a variant of
//! the trait that not only allows later requests to be sent on the same token (as is necessary to
//! orderly close observations) but also to send a family of requests on the same "channel" --
//! however, that would be a rather nice thing.
#![no_std]

use core::future::Future;

use coap_message::{MinimalWritableMessage, ReadableMessage};

/// Interface describing a CoAP request a client wants to send
///
/// This interface requires describes the request itself, and is passed to the stack in
/// [Stack::request].
///
/// Where possible, implementers should keep the [build_request] and [process_response] async
/// functions short, ideally not having any await points at all. The interface is defined to allow
/// slow interfaces to read into the buffer already allocated by the stack without blocking the
/// remaining operations of the stack.
// Placing the type generic on the trait level instead of on the function level.
//
// This creates some weird cases where rather than using object.method syntax we need `<_ as
// coap_request::Request<S>>::process_response(&mut self.previous, res, carry)` monstrosities. But
// the benefit that all of a sudden we can `impl Request for X where S::ResponseMessage:
// SomeExtraTrait` seems to be worth it.
pub trait Request<S: Stack + ?Sized> {
    type Output;
    type Carry;

    /// Build the request message
    ///
    /// The stack may run this multiple times if it requires retransmissions. This function may
    /// produce different results, and the response handler must be prepared to process the
    /// response to any of the requests.
    fn build_request(
        &mut self,
        request: &mut S::RequestMessage<'_>,
    ) -> impl Future<Output = Result<Self::Carry, S::RequestUnionError>>;

    /// Process the response message
    ///
    /// The stack calls this once, with any of the Carry produced by [build_request] (which as
    /// pointed out there does not indicate whether it was precisely *that* Carry's request that
    /// is being responded to).
    fn process_response(
        &mut self,
        response: &S::ResponseMessage<'_>,
        carry: Self::Carry,
    ) -> impl Future<Output = Self::Output>;
}

/// The CoAP stack provided by a CoAP library
///
/// Acting on this requires an exclusive reference `&mut self`. Stacks that support multiple
/// concurrent requests can allow cloning the stack, or implement this on a shared reference
/// `&self`.
///
/// In its current form, this takes no "remote address" argument, nor arguments that indicate
/// whether the request should be sent reliably. It is ecpected that stacks provide a builder like
/// this:
///
/// ```ignore
/// let wkc = stack
///     .to("coap://example.com".parse().unwrap())
///     .reliably()
///     .request(GetWellKnownCore)
///     .await
/// ```
pub trait Stack {
    /// Directly accessible error type of the request messages
    ///
    /// This is its own associated type because picking it from the RequestMessage would give it a
    /// lifetime, and things get complicated.
    type RequestUnionError: core::fmt::Debug;

    /// Type of message the client will write its request into
    type RequestMessage<'a>: MinimalWritableMessage<UnionError = Self::RequestUnionError>
    where
        Self: 'a;
    /// Type of message the client will read the response from
    type ResponseMessage<'a>: ReadableMessage
    where
        Self: 'a;
    /// Error indicating an error at some point below the CoAP request-reponse mechanism
    ///
    /// This can for example be a failure to establish a connection, or an invalid message from the
    /// peer. Few transports may use an infallible type here: While they may do ample checks at
    /// `.to()` time (eg. already starting a connection), any protocol error may wind up here. (A
    /// notable exception is a Rust typed CoAP loopback device).
    type TransportError: core::fmt::Debug;

    fn request<Req: Request<Self>>(
        &mut self,
        request: Req,
    ) -> impl Future<Output = Result<Req::Output, Self::TransportError>>;
}