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
//! Use format strings to create strongly-typed data pack/unpack interfaces (inspired by Python's `struct` library).
//!
//!
//! # Installation
//!
//! Add this to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! structure = "0.1"
//! ```
//!
//! And this to your crate root:
//!
//! ```rust
//! #[macro_use]
//! extern crate structure;
//!
//! # fn main() {}
//! ```
//!
//! # Examples
//!
//! ```rust
//! # #[macro_use]
//! # extern crate structure;
//! # fn foo() -> std::io::Result<()> {
//! // Two `u32` and one `u8`
//! let s = structure!("2IB");
//! let buf: Vec<u8> = s.pack(1, 2, 3)?;
//! assert_eq!(buf, vec![0, 0, 0, 1, 0, 0, 0, 2, 3]);
//! assert_eq!(s.unpack(buf)?, (1, 2, 3));
//! # Ok(())
//! # }
//! # fn main() {
//! # foo().unwrap();
//! # }
//! ```
//!
//! It's useful to use `pack_into` and `unpack_from` when using types that implement `Write` or `Read`.
//! The following example shows how to send a `u32` and a `u8` through sockets:
//!
//! ```rust
//! # #[macro_use]
//! # extern crate structure;
//! # fn foo() -> std::io::Result<()> {
//! use std::net::{TcpListener, TcpStream};
//! let listener = TcpListener::bind("127.0.0.1:0")?;
//! let mut client = TcpStream::connect(listener.local_addr()?)?;
//! let (mut server, _) = listener.accept()?;
//! let s = structure!("IB");
//! s.pack_into(&mut client, 1u32, 2u8)?;
//! let (n, n2) = s.unpack_from(&mut server)?;
//! assert_eq!((n, n2), (1u32, 2u8));
//! # Ok(())
//! # }
//! # fn main() {
//! # foo().unwrap();
//! # }
//! ```
//!
//! # Format Strings
//!
//! ## Endianness
//!
//! By default, the endianness is big-endian. It could be determined by specifying one of the
//! following characters at the beginning of the format:
//!
//! Character | Endianness
//! --------- | ----------
//! '=' | native (target endian)
//! '<' | little-endian
//! '>' | big-endian
//! '!' | network (= big-endian)
//!
//! ## Types
//!
//! Character | Type
//! --------- | ----
//! 'b' | `i8`
//! 'B' | `u8`
//! '?' | `bool`
//! 'h' | `i16`
//! 'H' | `u16`
//! 'i' | `i32`
//! 'I' | `u32`
//! 'q' | `i64`
//! 'Q' | `u64`
//! 'f' | `f32`
//! 'd' | `f64`
//! 's' | `&[u8]`
//! 'S' | `&[u8]`
//! 'P' | `*const c_void`
//! 'x' | padding (1 byte)
//!
//! * Any format character may be preceded by an integral repeat count. For example, the format string '4h'
//! means exactly the same as 'hhhh'.
//! * 'P' may be follow by a `<type>`, so `"P<u32>"` means a pointer to u32 (`*const u32`).
//! * When 's' is packed, its value can be smaller than the size specified in the format,
//! and the rest will be filled with zeros. For instance:
//!
//! ```rust
//! # #[macro_use]
//! # extern crate structure;
//! # fn foo() -> std::io::Result<()> {
//! assert_eq!(structure!("3s").pack(&[8, 9])?, vec![8, 9, 0]);
//! # Ok(())
//! # }
//! # fn main() {
//! # foo().unwrap();
//! # }
//! ```
//!
//! * Unlike 's', 'S' is a fixed-size buffer, so the size of its value must be exactly the size
//! specified in the format.
//! * By default, 's' and 'S' are buffers of one byte. To create a fixed-sized buffer with ten bytes,
//! the format would be "10S".
//! * On unpack, 'x' skips a byte. On pack, 'x' always writes a null byte. To skip multiple bytes,
//! prepend the length like in "10x".
//!
//! # Differences from Python struct library
//!
//! While the format strings look very similar to Python's `struct` library, there are a few differences:
//!
//! * Numbers' byte order is big-endian by default (e.g. u32, f64...).
//! * There is no alignment support.
//! * In addition to 's' (buffer) format character, that when packed, its value can be smaller than
//! the size specified in the format, there is the 'S' format character, that the size of its value must
//! be exactly the size specified in the format.
//! * The type of a pointer is `c_void` by default, but can be changed.
//! * 32 bit integer format character is only 'I'/'i' (and not 'L'/'l').
//! * structure!() macro takes a literal string as an argument.
//! * It's called `structure` because `struct` is a reserved keyword in Rust.
#[macro_use]
extern crate proc_macro_hack;
#[doc(hidden)]
pub extern crate byteorder;
// Allow the "unused" #[macro_use] because there is a different un-ignorable
// warning otherwise:
//
// proc macro crates and `#[no_link]` crates have no effect without `#[macro_use]`
#[allow(unused_imports)]
#[macro_use]
extern crate structure_macro_impl;
#[doc(hidden)]
pub use structure_macro_impl::*;
proc_macro_expr_decl! {
structure! => structure_impl
}