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 its [rust-gcoap example](https://github.com/RIOT-OS/RIOT/tree/master/examples/rust-gcoap),
44//!     the application runs the no_std part of the demo applications on RIOT's own gcoap
45//!     implementation.
46//!
47//! Usage
48//! -----
49//!
50//! The examples are all configured to run a selection of the applications; which they are depends
51//! on the selected features.
52//!
53//! For minimal operation, run the examples as
54//!
55//! ```sh
56//! $ cargo run --example EXNAME --features example-EXNAME
57//! ```
58//!
59//! where `EXNAME` is substituted with any of the examples -- currently `coaplite`, `coap_crate` or
60//! `std_embedded_nal_minicoapserver`.
61//!
62//! To explore all features, just run with
63//!
64//! ```sh
65//! $ cargo run --example EXNAME --all-features
66//! ```
67//!
68//! which, for example, adds access to a system [::log].
69//!
70//! All the same can be accessed, for example, by using [aiocoap-client]:
71//!
72//! ```sh
73//! $ aiocoap-client coap://localhost/.well-known/core
74//! # application/link-format content was re-formatted
75//! <>; ct=0; title="Landing page",
76//! </time>; ct=0; title=Clock,
77//! </poem>; sz=1338,
78//! </cbor/1>; ct=60,
79//! </cbor/2>; ct=60,
80//! </cbor>; ct=60,
81//! </message/warn>; title="POST warning texts here",
82//! </message/info>; title="POST info texts here",
83//! </log>; title="Most recent log messages"; if="tag:riot-os.org,2021:ser-out"
84//!
85//! $ aiocoap-client coap://localhost/cbor
86//! # CBOR message shown in Diagnostic Notation
87//! {0: false, 1: 32, 2: "Hello", 3: [1, 2, 3]}
88//! ```
89//!
90//! The `/log` resource is rather hard to use manually, but there is a [tool for
91//! it](https://pypi.org/project/coap-console/):
92//!
93//! ```sh
94//! $ pipx run coap-console coap://localhost
95//! INFO Server is ready.
96//! ```
97//!
98//! This produces a continuous output of log activity as it happens; you can add entries from
99//! another terminal using:
100//!
101//! ```sh
102//! $ aiocoap-client coap://localhost/message/info -m POST --payload "This will be shown in a moment"
103//! ```
104//!
105//! [coap-message]: https://crates.io/crates/coap-message
106//! [aiocoap-client]: https://aiocoap.readthedocs.io/en/latest/installation.html
107#![no_std]
108
109#[cfg(feature = "std")]
110extern crate std;
111
112#[cfg(feature = "alloc")]
113extern crate alloc;
114
115pub mod cbor;
116
117pub mod helloworld;
118
119#[cfg(feature = "with-log")]
120pub mod log;
121#[cfg(not(feature = "with-log"))]
122pub mod log {
123    /// A never-ish type that allows having a single full_application_tree function independent of
124    /// whether things are built with-log or without.
125    pub enum Log {}
126}
127
128/// Build a handler that contains all the demo applications
129///
130/// Note that no log::Log is created because that needs to be global, created-only-once and
131/// available as early in the application start-up as possible as per the log crate's design; you'd
132/// have to pass one in if you want to view the log through CoAP.
133///
134/// The resources /message/info and /message/warn for creating log messages will
135/// be available independently thereof as long as the with-log feature is active.
136pub fn full_application_tree(
137    main_log: Option<&'static crate::log::Log>,
138) -> impl coap_handler::Handler + coap_handler::Reporting {
139    #[cfg(feature = "with-log")]
140    use crate::log::*;
141    use cbor::*;
142    use helloworld::*;
143
144    #[cfg(feature = "std")]
145    let shared_cbor: std::sync::Arc<std::sync::Mutex<MyCBOR>> = Default::default();
146
147    use coap_handler_implementations::{
148        HandlerBuilder, ReportingHandlerBuilder, TypeHandler, new_dispatcher, with_get_put_fetch,
149    };
150
151    use coap_handler::Attribute::*;
152
153    let handler = new_dispatcher()
154        .at_with_attributes(&[], &[Ct(0), Title("Landing page")], WELCOME)
155        .at_with_attributes(&["time"], &[Ct(0), Title("Clock")], TIME)
156        .at_with_attributes(&["poem"], &[Sz(POEM_TEXT_LEN)], POEM);
157    #[cfg(feature = "std")]
158    let handler = handler
159        .at_with_attributes(
160            &["cbor", "1"],
161            &[Ct(60)],
162            TypeHandler::new_minicbor_2(with_get_put_fetch(TryingThroughMutex(
163                shared_cbor.clone(),
164            ))),
165        )
166        .at_with_attributes(
167            &["cbor", "2"],
168            &[Ct(60)],
169            TypeHandler::new_minicbor_2(with_get_put_fetch(TryingThroughMutex(shared_cbor))),
170        );
171    let handler = handler.at_with_attributes(
172        &["cbor"],
173        &[Ct(60)],
174        TypeHandler::new_minicbor_2(with_get_put_fetch(MyCBOR::default())),
175    );
176
177    #[cfg(feature = "with-log")]
178    let handler = handler
179        .at_with_attributes(
180            &["message", "warn"],
181            &[Title("POST warning texts here")],
182            LogMessagePostHandler::new_warn(),
183        )
184        .at_with_attributes(
185            &["message", "info"],
186            &[Title("POST info texts here")],
187            LogMessagePostHandler::new_info(),
188        )
189        .at_with_attributes(
190            &["log"],
191            // Adding `.at()` would work too, but we wouldn't get to set a title.
192            &[
193                Title("Most recent log messages"),
194                Interface("tag:riot-os.org,2021:ser-out"),
195            ],
196            main_log.map(|l| l.handler()),
197        );
198    #[cfg(not(feature = "with-log"))]
199    let _ = main_log; // Discarding what's sure to be None (because Ok can never be)
200
201    handler.with_wkc()
202}