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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
//! # Mavio
//!
//! Minimalistic library for transport-agnostic [MAVLink](https://mavlink.io/en/) communication.
//! It supports `no-std` (and `no-alloc`) targets.
//!
//! <span style="font-size:24px">[πΊπ¦](https://mavka.gitlab.io/home/a_note_on_the_war_in_ukraine/)</span>
//! [](https://gitlab.com/mavka/libs/mavio)
//! [](https://crates.io/crates/mavio)
//! [](https://docs.rs/mavio/latest/mavio/)
//! [](https://gitlab.com/mavka/libs/mavio/-/issues/)
//!
//! This library is a part of [Mavka](https://mavka.gitlab.io/home/) toolchain. It focuses on I/O
//! and uses [MAVSpec](https://gitlab.com/mavka/libs/mavspec) to generate MAVLink dialects.
//!
//! # Features
//!
//! Mavio is focused on I/O primitives for MAVLink that can be used in `no-std` and `no-alloc`
//! environments. Since Mavio is designed as a building block for more sophisticated tools,
//! it includes the absolute minimum of functionality required for correct communication with everything
//! that speaks MAVLink protocol.
//!
//! ## Basic capabilities
//!
//! * Mavio supports both `MAVLink 1` and `MAVLink 2` protocol versions.
//! * Provides intermediate MAVLink packets decoding with [`Frame`] that contain only header,
//! checksum, and signature being deserialized. Which means that a client doesn't have to decode
//! the entire message for routing and verification.
//! * Supports optional high-level message decoding by utilizing MAVLink abstractions generated by
//! [MAVSpec](https://crates.io/crates/mavspec).
//! * Includes standard MAVLink dialects enabled by cargo features.
//! * Implements message verification via checksum.
//! * Provides a mechanism for message sequencing through [`Endpoint::next_frame`], that encodes a
//! MAVLink message into a [`Frame`] with a correct sequence as required by MAVLink
//! [protocol](https://mavlink.io/en/guide/serialization.html).
//! * Includes tools for [message signing](https://mavlink.io/en/guide/message_signing.html).
//!
//! ## Flexible approach to I/O
//!
//! Mavio is designed to be flexible and useful in different scenarios.
//!
//! * It can work without any MAVLink dialect at all (for intermediate decoding and proxying).
//! * Includes support for custom payload decoders and encoders. Which means that clients are not
//! bounded by abstractions generated by [MAVSpec](https://crates.io/crates/mavspec).
//! * Uses implementation-agnostic I/O primitives with a variety of I/O [adapters](io::adapters).
//! * Provides synchronous I/O adapters for [embedded-io](https://docs.rs/embedded-io/) and
//! [`std::io`].
//! * Supports asynchronous I/O by providing lightweight adapters for
//! [embedded-io-async](https://docs.rs/embedded-io-async/), [Tokio](https://tokio.rs), and
//! [futures-rs](https://docs.rs/futures/).
//!
//! ## MAVLink protocol support
//!
//! Such features are mainly related to the functionality provided by other parts of
//! [Mavka](https://mavka.gitlab.io/home/) toolchain. These tasks are mainly performed by
//! [MAVInspect](https://crates.io/crates/mavinspect) and [MAVSpec](https://crates.io/crates/mavspec)
//! (a message definition parser and code-generator respectively). In particular:
//!
//! * Mavio allows using custom MAVLink dialects. This includes both dialects generated from XML
//! definitions and adhoc dialects defined with pure Rust.
//! * Respects dialect inheritance. Messages defined in one dialect are not redefined upon inclusion
//! into another dialect. This means that if you have a message `M` from dialect `A` being
//! included by dialect `B`, it guarantees that you can use Rust structs for message `M` with both
//! of the dialects. The same is true for MAVLink enums and bitmasks.
//! * Provides optional support for MAVLink [microservices](https://mavlink.io/en/services/) as
//! subdialects and helpers to work with them such as
//! [mission file format](https://mavlink.io/en/file_formats/#mission_plain_text_file).
//!
//! ## Serialization / deserialization and FFI
//!
//! Mavio provides support for [serde](https://serde.rs) and [specta](https://specta.dev). The
//! latter is supported only for `std` targets. All MAVLink entities are fully supported.
//!
//! ## Out of scope
//!
//! There are few *stateful* features required by MAVLink protocol this library intentionally does
//! not implement and leaves for the client:
//!
//! * Sending automatic [heartbeats](https://mavlink.io/en/services/heartbeat.html). This is
//! required by most of the clients which would consider nodes without heartbeats as inactive or
//! invalid.
//! * Stateful timestamp management for [message signing](https://mavlink.io/en/guide/message_signing.html)
//! (ensuring that two messages are not sent with the same timestamp).
//! * Retry logic for failed connections.
//!
//! All such features are provided by [Maviola](https://crates.io/crates/maviola).
//!
//! # Usage
//!
//! This library exposes [`Sender`] and [`Receiver`] to send and receive instances of MAVLink
//! [`Frame`]. Frames contain an encoded message body in [`Frame::payload`] and additional fields
//! (such as `sequence` or `system_id`) as required by [MAVLink specification](https://mavlink.io/en/guide/serialization.html).
//! Once a frame is received, it can be decoded into a specific `Message`. Frame decoding requires
//! dialect specification which can be either generated manually by using
//! [MAVSpec](https://gitlab.com/mavka/libs/mavspec) or by enabling built-in
//! [dialect features](#dialects).
//!
//! Upon receiving or building a frame, it can be converted into a protocol-agnostic [`MavFrame`],
//! that hides the generic version parameter of a [`Frame`].
//!
//! ## Receive
//!
//! Connect to TCP port and receive the first 10 MAVLink frames, decode any received
//! [HEARTBEAT](https://mavlink.io/en/messages/common.html#HEARTBEAT) messages.
//!
//! ```rust,no_run
//! # #[cfg(not(all(feature = "dlct-minimal", feature = "std")))]
//! # fn main() {}
//! # #[cfg(all(feature = "dlct-minimal", feature = "std"))]
//! # fn main() -> mavio::error::Result<()> {
//! use std::net::TcpStream;
//!
//! use mavio::prelude::*;
//! use mavio::dialects::minimal as dialect;
//!
//! use dialect::Minimal;
//!
//! // Create a TCP client receiver
//! let reader = StdIoReader::new(TcpStream::connect("0.0.0.0:5600")?);
//! let mut receiver = Receiver::versionless(reader);
//!
//! for i in 0..10 {
//! let frame = receiver.recv()?;
//!
//! // Validate MAVLink frame
//! if let Err(err) = frame.validate_checksum::<Minimal>() {
//! eprintln!("Invalid checksum: {err:?}");
//! continue;
//! }
//!
//! if let Ok(Minimal::Heartbeat(msg)) = frame.decode() {
//! println!(
//! "HEARTBEAT #{}: mavlink_version={:#?}",
//! frame.sequence(),
//! msg.mavlink_version,
//! );
//! }
//! }
//!
//! # Ok(())
//! # }
//! ```
//!
//! ## Send
//!
//! Connect to TCP port and send 10 [HEARTBEAT](https://mavlink.io/en/messages/common.html#HEARTBEAT) messages using
//! `MAVLink 2` protocol.
//!
//! ```rust,no_run
//! # #[cfg(not(all(feature = "dlct-minimal", feature = "std")))]
//! # fn main() {}
//! # #[cfg(all(feature = "dlct-minimal", feature = "std"))]
//! # fn main() -> mavio::error::Result<()> {
//! use std::net::TcpStream;
//!
//! use mavio::prelude::*;
//! use mavio::dialects::minimal as dialect;
//! use dialect::enums::{MavAutopilot, MavModeFlag, MavState, MavType};
//!
//! // Create a TCP client sender
//! let writer = StdIoWriter::new(TcpStream::connect("0.0.0.0:5600")?);
//! let mut sender = Sender::new(writer);
//!
//! // Create an endpoint that represents a MAVLink device speaking `MAVLink 2` protocol
//! let endpoint = Endpoint::v2(MavLinkId::new(15, 42));
//!
//! // Create a message
//! let message = dialect::messages::Heartbeat {
//! type_: MavType::FixedWing,
//! autopilot: MavAutopilot::Generic,
//! base_mode: MavModeFlag::TEST_ENABLED & MavModeFlag::CUSTOM_MODE_ENABLED,
//! custom_mode: 0,
//! system_status: MavState::Active,
//! mavlink_version: dialect::Minimal::version().unwrap(),
//! };
//! println!("MESSAGE: {message:?}");
//!
//! for i in 0..10 {
//! // Build the next frame for this endpoint.
//! // All required fields will be populated, including frame sequence counter.
//! let frame = endpoint.next_frame(&message)?;
//!
//! sender.send(&frame)?;
//! println!("FRAME #{} sent: {:#?}", i, frame);
//! }
//!
//! # Ok(())
//! # }
//! ```
//!
//! # I/O adapters
//!
//! We provide lightweight [`Read`](io::Read) / [`Write`](io::Write) and
//! [`AsyncRead`](io::AsyncRead) / [`AsyncWrite`](io::AsyncWrite) pairs for synchronous and
//! asynchronous I/O.
//!
//! Mavio packages a set of I/O adapters available under the corresponding feature flags:
//!
//! - `embedded-io` β [`io::EmbeddedIoReader`] / [`io::EmbeddedIoWriter`]
//! - `embedded-io-async` β [`io::EmbeddedIoAsyncReader`] / [`io::EmbeddedIoAsyncWriter`]
//! - `std` β [`io::StdIoReader`] / [`io::StdIoWriter`]
//! - `tokio` β [`io::TokioReader`] / [`io::TokioWriter`]
//! - `futures` β [`io::FuturesReader`] / [`io::FuturesWriter`]
//!
//! See [`adapters`](io::adapters) for details.
//!
//! # MAVLink protocol
//!
//! We use [MAVSpec](https://crates.io/crates/mavspec) to generate MAVLink entities and additional
//! abstractions. These entities are bundled with Mavio for a better user experience and to prevent
//! discrepancies in crate versions.
//!
//! ## Dialects
//!
//! Standard MAVLink dialect can be enabled by the corresponding feature flags (`dlct-<dialect name>`).
//!
//! * [`minimal`]((https://mavlink.io/en/messages/minimal.html)) β minimal dialect required to
//! expose your presence to other MAVLink devices.
//! * [`standard`](https://mavlink.io/en/messages/standard.html) β a superset of the `minimal` dialect
//! that is expected to be used by almost all flight stacks.
//! * [`common`](https://mavlink.io/en/messages/common.html) β minimum viable dialect with most of
//! the features, a building block for other future-rich dialects.
//! * [`ardupilotmega`](https://mavlink.io/en/messages/common.html) β feature-full dialect used by
//! [ArduPilot](http://ardupilot.org). In most cases this dialect is the go-to choice if you want
//! to recognize almost all MAVLink messages used by existing flight stacks.
//! * [`all`](https://mavlink.io/en/messages/all.html) β meta-dialect which includes all other
//! standard dialects including those which were created for testing purposes. It is guaranteed
//! that namespaces of the dialects in the ` all ` family do not collide.
//! * Other dialects from MAVLink XML [definitions](https://github.com/mavlink/mavlink/tree/master/message_definitions/v1.0):
//! `asluav`, `avssuas`, `csairlink`, `cubepilot`, `development`, `icarous`, `matrixpilot`,
//! `paparazzi`, `ualberta`, `uavionix`. These do not include `python_array_test` and `test`
//! dialects which should be either generated manually or as a part of `all` meta-dialect.
//!
//! Use [`Frame::decode`] with an implicit or explicit generic parameter of a dialect. For example:
//!
//! ```rust
//! # #[cfg(not(all(feature = "dlct-common", feature = "std")))]
//! # fn main() {}
//! # #[cfg(all(feature = "dlct-common", feature = "std"))]
//! # fn main() -> mavio::error::Result<()> {
//! use mavio::dialects::common as dialect;
//! use dialect::{Common, messages::Heartbeat};
//! use mavio::prelude::*;
//!
//! let frame: Frame<V2> = /* obtain a frame */
//! # Frame::builder()
//! # .version(V2)
//! # .system_id(1)
//! # .component_id(0)
//! # .sequence(0)
//! # .message(&Heartbeat::default())?
//! # .build();
//!
//! // Decode MavLink frame into a dialect message:
//! match frame.decode()? {
//! Common::Heartbeat(msg) => {
//! /* process heartbeat */
//! }
//! /* process other messages */
//! # _ => { unreachable!(); }
//! };
//! # Ok(())
//! # }
//! ```
//!
//! ### Direct decoding into a message
//!
//! When memory footprint is critical, using dialect enums is suboptimal. In this case you can use
//! [`Frame::decode_message`] to decode a frame into a specific message:
//!
//! ```rust
//! # #[cfg(not(all(feature = "dlct-common", feature = "std")))]
//! # fn main() {}
//! # #[cfg(all(feature = "dlct-common", feature = "std"))]
//! # fn main() -> mavio::error::Result<()> {
//! use mavio::dialects::common as dialect;
//! use dialect::{Common, messages::Heartbeat};
//! use mavio::prelude::*;
//!
//! let frame: Frame<V2> = /* obtain a frame */
//! # Frame::builder()
//! # .version(V2)
//! # .system_id(1)
//! # .component_id(0)
//! # .sequence(0)
//! # .message(&Heartbeat::default())?
//! # .build();
//!
//! // Decode MavLink frame into a dialect message:
//! match frame.message_id() {
//! Heartbeat::ID => {
//! let message = frame.decode_message::<Heartbeat>().unwrap();
//! /* process heartbeat */
//! }
//! /* process other messages */
//! # _ => { unreachable!(); }
//! };
//! # Ok(())
//! # }
//! ```
//!
//! ## Default dialect
//!
//! When standard MAVLink dialects are used and at least `dlct-minimal` Cargo feature is enabled,
//! this library exposes [`default_dialect`] and [`DefaultDialect`] entities that allow to access
//! the most feature-rich enabled MAVLink dialect. Other features such as [`microservices`] or
//! [microservice utils](microservices::utils) are based on this convention.
//!
//! The sequence of default dialects is the following (in the order of the increased completeness):
//!
//! - [`minimal`](dialects::minimal) β enabled by `dlct-minimal` feature flag
//! - [`standard`](dialects::standard) β enabled by `dlct-standard` feature flag
//! - [`common`](dialects::common) β enabled by `dlct-common` feature flag
//! - [`ardupilotmega`](dialects::ardupilotmega) β enabled by `dlct-ardupilotmega` feature flag
//! - [`all`](dialects::all) β enabled by `dlct-all` feature flag
//!
//! ## Microservices
//!
//! Mavio re-exports MAVLink [microservices](https://mavlink.io/en/services/) from
//! [MAVSpec](https://crates.io/crates/mavspec). To control which microservices you want to generate,
//! use `msrv-*` feature flags family. Check MAVSpec [API docs](https://docs.rs/mavspec/latest) for
//! details.
//!
//! At the moment, microservices are generated only for [`default_dialect`] and can be accessed
//! through [`microservices`].
//!
//! ### Microservice utils
//!
//! In addition, Mavio re-exports extra tools for working with microservices. These tools can be
//! enabled by `msrv-utils-*` feature flags and available in [`microservices::utils`] module and
//! bundled inside the corresponding microservice.
//!
//! <section class="warning">
//! `msrv-utils-*` are considered unstable for now! Use the ` unstable ` feature flag to enable them.
//! </section>
//!
//! ## Message definitions
//!
//! It is possible to bundle message definitions generated by [MAVInspect](https://crates.io/crates/mavinspect)
//! into the [`definitions`] module. This can be useful for ground control stations that require to present the
//! user with the descriptions of MAVLink entities.
//!
//! To enable MAVLink definitions bundling, use the ` definitions ` feature flag.
//!
//! <section class="warning">
//! Message definitions are available only with the ` std ` feature enabled. Otherwise, this will
//! cause the build to fail.
//! </section>
//!
//! ## Metadata
//!
//! MAVSpec can generate additional metadata such as MAVLink enum entry names. This can be a useful
//! addition to MAVlink [`definitions`] when ground stations are considered. To enable metadata
//! support, use the `metadata` feature flag.
//!
//! # Caveats
//!
//! The API is straightforward and generally stable; however, some caution is required when working
//! with edge-cases.
//!
//! ## Unsafe Features
//!
//! This library does not use unsafe Rust, however, certain manipulations on MAVLink frames, if not
//! performed carefully, could lead to data corruption and undefined behavior. All such operations
//! are covered by `unsafe` cargo features and marked with <sup>`β `</sup> in the documentation.
//!
//! Most of the unsafe operations are related to updating existing frames in-place. In general,
//! situations when you need mutable access to frames are rare. If your use-case does not strictly
//! require such manipulations, we suggest refraining from using functionality hidden under the
//! `unsafe` feature flags.
//!
//! ## Unstable Features
//!
//! Certain features are considered unstable and available only when the ` unstable ` feature flag
//! is enabled. Unstable features are marked with <sup>`β`</sup> and could be changed in future
//! versions.
//!
//! ## Incompatible Features
//!
//! - [Specta](https://crates.io/crates/specta) requires the ` std ` feature to be enabled.
//! - MAVlink [`definitions`] requires the ` std ` feature to be enabled.
//!
//! ## Binary Size
//!
//! For small applications that use only a small subset of messages, avoid using dialect enums as
//! they contain all message variants. Instead, decode messages directly from frames:
//!
//! ```rust
//! # #[cfg(not(all(feature = "dlct-common", feature = "std")))]
//! # fn main() {}
//! # #[cfg(all(feature = "dlct-common", feature = "std"))]
//! # fn main() -> Result<(), mavio::error::SpecError> {
//! use mavio::dialects::common as dialect;
//! use dialect::messages::Heartbeat;
//! use mavio::prelude::*;
//!
//! let frame: Frame<V2> = /* obtain a frame */
//! # Frame::builder()
//! # .version(V2)
//! # .system_id(1)
//! # .component_id(0)
//! # .sequence(0)
//! # .message(&Heartbeat::default()).unwrap()
//! # .build();
//!
//! // Use only specific messages:
//! match frame.message_id() {
//! Heartbeat::ID => {
//! let msg = Heartbeat::try_from(frame.payload())?;
//! /* process heartbeat */
//! }
//! /* process other messages */
//! # _ => { unreachable!(); }
//! };
//! # Ok(())
//! # }
//! ```
//!
//! This will help compiler to throw away unnecessary pieces of code.
//!
//! Additionally, you may use [`microservices`] as well as [`microservices::utils`] to reduce the
//! API surface you are interacting with.
//!
//! # Feature flags
//!
//! In most of the cases you will be interested in `dlct-*` features to access MAVLink [`dialects`],
//! I/O [`adapters`](io::adapters), and `alloc` / `std` target specification. However, a more
//! fine-grained control may be required.
//
extern crate alloc;
extern crate core;
pub use crate;
pub use crate;
pub use ;
pub use ;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// Default MAVLink dialect module
///
/// Similar to [`DefaultDialect`] but provides access to a dialect module instead of dialect itself.
///
/// See [`DefaultDialect`] to learn about logic behind choosing a default dialect.
///
/// # Usage
///
/// ```rust,no_run
/// use mavio::default_dialect;
///
/// let message = default_dialect::messages::Heartbeat::default();
/// ```
///
/// Requires at least `dlct-minimal` dialect feature flag to be enabled.
///
/// Re-exported from [`mavspec::rust::default_dialect`].
///
/// ---
pub use default_dialect;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// Default MAVLink dialect
///
/// The rules for determining the default dialect are defined by the following order of canonical dialect inclusion:
///
/// [`all`](https://mavlink.io/en/messages/all.html) >
/// [`ardupilotmega`](https://mavlink.io/en/messages/common.html) >
/// [`common`](https://mavlink.io/en/messages/common.html) >
/// [`standard`]((https://mavlink.io/en/messages/standard.html))
/// [`minimal`]((https://mavlink.io/en/messages/minimal.html))
///
/// That means that if you enabled `dlct-ardupilotmega` dialect but not `all`, then the former is the
/// most general canonical dialect, and it will be chosen as a default one.
///
/// Requires at least a `dlct-minimal` dialect feature flag to be enabled.
///
/// Re-exported from [`mavspec::rust::DefaultDialect`].
///
/// ---
pub use DefaultDialect;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// MAVLink dialects
///
/// These dialects are generated by [MAVSpec](https://crates.io/crates/mavspec).
///
/// Each dialect belongs to a specific module, such as:
///
/// - [`minimal`](crate::dialects::minimal)
/// - [`common`](crate::dialects::common)
/// - [`ardupilotmega`](crate::dialects::ardupilotmega)
/// - ... and so on
///
/// Re-exported from [`mavspec::rust::dialects`].
///
/// ---
pub use dialects;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// MAVLink [microservices](https://mavlink.io/en/services/)
///
/// Enabled by `msrv-*` feature flags, additional tools are available as [`microservices::utils`]
/// via `msrv-utils-*` feature flags (requires `unstable` feature).
///
/// Re-exported from [`mavspec::rust::microservices`].
///
/// ---
pub use microservices;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// MAVLink message definitions
///
/// Requires `definitions` feature flag to be enabled.
///
/// <section class="warning">
/// Requires `std` feature flag to be enabled. Otherwise, the library won't compile.
/// </section>
///
/// Re-exported from [`mavspec::definitions`].
///
/// ---
pub use definitions;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// MAVSpec procedural macros
///
/// Since derive macros relies on entities from [`mavspec::rust::spec`], you have to import
/// [`mavspec`] or use [`prelude`]. For example:
///
/// ```rust
/// #[cfg(feature = "derive")]
/// # {
/// use mavio::prelude::*; // This is necessary!!!
/// use mavio::derive::Enum;
///
/// #[derive(Enum)]
/// #[repr(u8)]
/// #[derive(Copy, Clone, Debug, Default)]
/// enum CustomEnum {
/// #[default]
/// DEFAULT = 0,
/// OptionA = 1,
/// OptionB = 2,
/// }
/// # }
/// ```
///
/// Requires `derive` feature flag to be enabled.
///
/// Re-exported from [`mavspec::rust::derive`].
///
/// ---
pub use derive;
/// <sup>[`mavspec`](https://crates.io/crates/mavspec)</sup>
/// [MAVSpec](https://crates.io/crates/mavspec) re-exported
///
/// We re-export MAVSpec in order to simplify interoperability with the tools provided by this
/// library.
///
/// For example, [`derive`](mod@derive) proc macros depends on [`mavspec::rust::spec`] being
/// accessible.
///
/// ---
pub use mavspec;
compile_error!;
compile_error!;