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}