coap_message_demos/
lib.rs

1//! This crate contains demo applications for CoAP on Rust
2//!
3//! All demos use the ecosystem around the [coap-message] crate. They come in two variations:
4//!
5//! * "applications" contain code that would typically be the high-level code that executes
6//!   business logic.
7//!
8//!   They are a mix of standalone resource implementations, collections thereof into a
9//!   whole-server handler, and possibly client code.
10//!
11//!   They reside in the `src/` directory, and are available as library modules. This allows
12//!   integrating them into other demo code, eg. into examples of a coap-message implementation.
13//!
14//! * "examples" are the stand-alone executable binaries using various backends.
15//!
16//!   They pick suitable applications, and wrap them with a CoAP implementation of choice into a
17//!   program that can be run with `cargo run --example X`.
18//!
19//!   Currently, the examples in this crate show the use of:
20//!
21//!   * [coap-lite](https://crates.io/crates/coap-lite),
22//!     a building block for CoAP-over-UDP libraries, running directly on a socket in the example.
23//!
24//!   * [the coap crate](https://crates.io/crates/coap),
25//!     which provides a full implementation, and can interface with coap-message by virtue of
26//!     using coap-lite as a backend.
27//!
28//!   * [embedded-nal-minimal-coapserver](https://crates.io/crates/embedded-nal-minimal-coapserver),
29//!     which implements CoAP-over-UDP on the Embedded Network Abstraction Layer
30//!     and processes messages through the [coap_handler] types.
31//!     For the example, it uses a std implementation of embedded-nal.
32//!
33//!   Examples that need larger ecosystem support and can not simply be launched natively by `cargo
34//!   run --example` are not included here, but show (maybe even better) what the coap-message
35//!   ecosystem is capable of providing:
36//!
37//!   * [verdigris](https://crates.io/crates/verdigris)
38//!     is an implementation of CoAP that runs in the web browser and uses CoAP-over-WebSockets.
39//!     It includes the demo applications in its color server sub-application, where they can be
40//!     accessed through a proxying Resource Directory.
41//!
42//!   * [RIOT](https://riot-os.org/) is an embedded operating system for the Internet of Things.
43//!     In the [examples of its Rust bindings](https://gitlab.com/etonomy/riot-examples/),
44//!     the `coap_through_embeddednal` application runs the no_std part of the demo applications
45//!     using the same embedded-nal-minimal-coapserver crate as the local example,
46//!     but using RIOT's sockets instead of POSIX sockets.
47//!
48//! Usage
49//! -----
50//!
51//! The examples are all configured to run a selection of the applications; which they are depends
52//! on the selected features.
53//!
54//! For minimal operation, run the examples as
55//!
56//! ```sh
57//! $ cargo +nightly run --example EXNAME --features example-EXNAME
58//! ```
59//!
60//! where `EXNAME` is substituted with any of the examples -- currently `coaplite`, `coap_crate` or
61//! `std_embedded_nal_minicoapserver`.
62//!
63//! To explore all features, just run with
64//!
65//! ```sh
66//! $ cargo +nightly run --example EXNAME --all-features
67//! ```
68//!
69//! which, for example, adds access to a system [::log].
70//!
71//! All the same can be accessed, for example, by using [aiocoap-client]:
72//!
73//! ```sh
74//! $ aiocoap-client coap://localhost/.well-known/core
75//! </>; ct="0"; title="Landing page",
76//! </time>; ct="0"; title="Clock",
77//! </cbor/1>; ct="60",
78//! </cbor/2>; ct="60",
79//! </cbor>; ct="60",
80//! </message/warning>; title="POST warning texts here",
81//! </message/info>; title="POST info texts here"
82//!
83//! $ aiocoap-client coap://localhost/cbor
84//! {'hidden': False, 'number': 32, 'label': 'Hello', 'list': [1, 2, 3]}
85//! ```
86//!
87//! [coap-message]: https://crates.io/crates/coap-message
88//! [aiocoap-client]: https://aiocoap.readthedocs.io/en/latest/installation.html
89#![no_std]
90
91#[cfg(feature = "std")]
92extern crate std;
93
94#[cfg(feature = "alloc")]
95extern crate alloc;
96
97pub mod cbor;
98
99pub mod helloworld;
100
101#[cfg(feature = "with-log")]
102pub mod log;
103#[cfg(not(feature = "with-log"))]
104pub mod log {
105    /// A never-ish type that allows having a single full_application_tree function independent of
106    /// whether things are built with-log or without.
107    pub enum Log {}
108}
109
110/// Build a handler that contains all the demo applications
111///
112/// Note that no log::Log is created because that needs to be global, created-only-once and
113/// available as early in the application start-up as possible as per the log crate's design; you'd
114/// have to pass one in if you want to view the log through CoAP.
115///
116/// The resources /message/info and /message/warn for creating log messages will
117/// be available independently thereof as long as the with-log feature is active.
118pub fn full_application_tree(
119    main_log: Option<&'static crate::log::Log>
120    ) -> impl coap_handler::Handler + coap_handler::Reporting {
121    #[cfg(feature = "with-log")]
122    use crate::log::*;
123    use cbor::*;
124    use helloworld::*;
125
126    #[cfg(feature = "std")]
127    let shared_cbor: std::sync::Arc<std::sync::Mutex<MyCBOR>> = Default::default();
128
129    use coap_handler_implementations::{new_dispatcher, HandlerBuilder, ReportingHandlerBuilder, TypeHandler};
130
131    use coap_handler::Attribute::*;
132
133    let handler = new_dispatcher()
134        .at_with_attributes(&[], &[Ct(0), Title("Landing page")], WELCOME)
135        .at_with_attributes(&["time"], &[Ct(0), Title("Clock")], TIME)
136        .at_with_attributes(&["poem"], &[Sz(POEM_TEXT_LEN)], POEM);
137    #[cfg(feature = "std")]
138    let handler = handler
139        .at_with_attributes(
140            &["cbor", "1"],
141            &[Ct(60)],
142            TypeHandler::new(TryingThroughMutex(shared_cbor.clone())),
143        )
144        .at_with_attributes(
145            &["cbor", "2"],
146            &[Ct(60)],
147            TypeHandler::new(TryingThroughMutex(shared_cbor)),
148        );
149    let handler = handler.at_with_attributes(
150        &["cbor"],
151        &[Ct(60)],
152        TypeHandler::<MyCBOR>::new(Default::default()),
153    );
154
155    #[cfg(feature = "with-log")]
156    let handler = handler
157        .at_with_attributes(
158            &["message", "warn"],
159            &[Title("POST warning texts here")],
160            LogMessagePostHandler::new_warn(),
161            )
162        .at_with_attributes(
163            &["message", "info"],
164            &[Title("POST info texts here")],
165            LogMessagePostHandler::new_info(),
166            )
167        .at_with_attributes(
168            &["log"],
169            // Adding `.at()` would work too, but we wouldn't get to set a title.
170            &[Title("Most recent log messages"), Interface("tag:riot-os.org,2021:ser-out")],
171            main_log.map(|l| l.handler()),
172        );
173    #[cfg(not(feature = "with-log"))]
174    let _ = main_log; // Discarding what's sure to be None (because Ok can never be)
175
176    handler
177        .with_wkc()
178}