anpp 2.0.0

A Rust implementation of the Advanced Navigation Packet Protocol
Documentation
//! 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 crate::{decoder::Decoder, packet::Packet};