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;