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
//! A Rust implementation of the *Advanced Navigation Packet Protocol*.
//!
//! The *Advanced Navigation Packet Protocol* is a low-overhead communication
//! protocol designed mainly for embedded systems. It provides a mechanism for
//! breaking a stream of data into packets (*framing*) while ensuring data
//! integrity via CRC-16 checksums.
//!
//! # Examples
//!
//! Each [`Packet`] is essentially a collection of bytes with an associated ID.
//! The communication protocol is transport-agnostic (it doesn't care if
//! you're using RS-232, ethernet, smoke signals, etc), you just create a
//! [`Packet`] and write it to a buffer that can be handed off to the
//! underlying transport mechanism.
//!
//! ```rust
//! # use anpp::Packet;
//! # use anpp::errors::EncodeError;
//! # use std::error::Error;
//! # fn main() -> Result<(), EncodeError> {
//! // create a packet
//! let pkt = Packet::with_data(42, b"A very important message")?;
//!
//! // then write it to an internal buffer
//! let mut buffer = [0; 512];
//! let bytes_written = pkt.write_to_buffer(&mut buffer)?;
//!
//! // and finally, you can send the information using your actual transport
//! // mechanism. In this case, RS-232.
//! send_serial_message(&buffer[..bytes_written])?;
//! # Ok(())
//! # }
//! # fn send_serial_message(_: &[u8]) -> Result<(), EncodeError> { Ok(()) }
//! ```
//!
//! In order to read a message on the other side, you'll need a [`Decoder`].
//! The idea is you write raw data into the `Decoder` and it'll keep returning
//! [`DecodeError::RequiresMoreData`] until it has received a complete packet.
//!
//! ```rust
//! # use anpp::{Packet, Decoder};
//! # use anpp::errors::{DecodeError, InsufficientCapacity};
//! # use std::error::Error;
//! # fn run() -> Result<(), Oops> {
//! // create the decoder
//! let mut dec = Decoder::new();
//!
//! // then read some data
//! let data = read_data_from_serial();
//!
//! // next you need to copy the data into the decoder
//! dec.push_data(&data)?;
//!
//! // Now the decoder has some bytes to work with, we can read back our packet
//! let pkt = dec.decode()?;
//!
//! assert_eq!(pkt.id(), 42);
//! assert_eq!(pkt.contents(), b"A very important message");
//! # Ok(())
//! # }
//! # fn read_data_from_serial() -> Vec<u8> {
//! #   let pkt = Packet::with_data(42, b"A very important message").unwrap();
//! #   let mut buffer = vec![0; pkt.total_length()];
//! #   pkt.write_to_buffer(&mut buffer).unwrap();
//! #   buffer
//! # }
//! # fn main() { run().unwrap() }
//! # // without cfg(feature = "std") we need a common error type :(
//! # #[derive(Debug)] struct Oops;
//! # use std::fmt::{self, Display, Formatter};
//! # impl Error for Oops {}
//! # impl Display for Oops {
//! #   fn fmt(&self, f: &mut Formatter) -> fmt::Result { unimplemented!() }
//! # }
//! # impl From<InsufficientCapacity> for Oops {
//! #   fn from(other: InsufficientCapacity) -> Oops { unimplemented!() }
//! # }
//! # impl From<DecodeError> for Oops {
//! #   fn from(other: DecodeError) -> Oops { unimplemented!() }
//! # }
//! ```
//!
//! # Feature Flags
//!
//! The `std` feature flag enables several nice-to-have features, including:
//!
//! - Implementations of `std::error::Error` for all error types
//! - The `Decoder` will use a dynamically sized buffer which can resize
//!   when it is full instead of throwing an error
//! - You can use the `std::io::Write` trait to write data into the `Decoder`
//!   and `Packet`
//! - A `write_to()` method is added to `Packet`, allowing you to write the
//!   `Packet` directly to anything that implements `std::io::Write`
//!
//! An example of using `std::io::Write` with a `Packet`:
//!
//! ```rust
//! # extern crate anpp;
//! # use anpp::Packet;
//! # use std::error::Error;
//! use std::io::Write;
//! # #[cfg(feature = "std")]
//! # fn main() -> Result<(), Box<Error>> {
//! // open our serial port again
//! let mut port = open_serial_port("/dev/TTYUSB1");
//!
//! // then create a packet
//! let mut pkt = Packet::with_data(42, b"This is a very important message")?;
//!
//! // oops, we forgot to finish the sentence!
//! pkt.write(b".")?;
//!
//! // now encode the packet and write it out over RS-232
//! pkt.write_to(&mut port)?;
//! # fn open_serial_port(_: &str) -> Vec<u8> { Vec::new() }
//! # Ok(())
//! # }
//! # #[cfg(not(feature = "std"))]
//! # fn main() {}
//! ```
//!
//! Likewise, `std::io::Write` makes it easy to work with a decoder.
//!
//! ```rust
//! # extern crate anpp;
//! # use anpp::{Decoder, Packet};
//! # use std::error::Error;
//! # use std::io::{Cursor, Read};
//! use std::io;
//!
//! # #[cfg(feature = "std")]
//! # fn main() -> Result<(), Box<Error>> {
//! let mut dec = Decoder::new();
//! let mut serial = open_serial_port("/dev/TTYUSB0");
//!
//! // `std::io::copy()` copies all data from a Reader to a Writer
//! let bytes_read = io::copy(&mut serial, &mut dec)?;
//!
//! let pkt = dec.decode()?;
//! assert_eq!(pkt.id(), 42);
//! # fn open_serial_port(_: &str) -> Box<Read> {
//! #   let pkt = Packet::with_data(42, b"A very important message").unwrap();
//! #   let mut buffer = vec![0; pkt.total_length()];
//! #   pkt.write_to_buffer(&mut buffer).unwrap();
//! #
//! #   Box::new(Cursor::new(buffer))
//! # }
//! # Ok(())
//! # }
//! # #[cfg(not(feature = "std"))]
//! # fn main() {}
//! ```
//!
//! [`Packet`]: struct.Packet.html
//! [`Decoder`]: struct.Decoder.html
//! [`DecodeError::RequiresMoreData`]: errors/enum.DecodeError.html#variant.RequiresMoreData

#![cfg_attr(not(feature = "std"), no_std)]
#![deny(
    missing_docs,
    missing_debug_implementations,
    missing_copy_implementations,
    trivial_casts,
    trivial_numeric_casts,
    unsafe_code,
    unstable_features,
    unused_import_braces,
    unused_qualifications
)]

#[cfg(feature = "std")]
extern crate core;

extern crate arrayvec;
extern crate byteorder;

#[cfg(all(test, not(feature = "std")))]
#[macro_use]
extern crate std;

mod decoder;
pub mod errors;
mod packet;
// only exported for benchmarking purposes
#[doc(hidden)]
pub mod utils;

pub use decoder::Decoder;
pub use packet::Packet;