coap-request 0.1.0

Interface to CoAP requests
Documentation
//! 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).
#![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. Gotta see if this
// makes anything easier.
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 = Self::Carry>;

    /// 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 {
    /// Type of message the client will write its request into
    type RequestMessage<'a>: MinimalWritableMessage
    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>>;
}