Skip to main content

packs/
lib.rs

1//! A [PackStream](https://7687.org/packstream/packstream-specification-1.html) implementation written
2//! in Rust.
3//!
4//! # API
5//! The trait [`Pack`](crate::packable::Pack) is for encoding, the trait [`Unpack`](crate::packable::Unpack)
6//! is for decoding. They abstracted over [`Write`](std::io::Write) and [`Read`](std::io::Read) respectively.
7//!
8//! The traits are implemented for some basic types as well as for a standard set of structs which come
9//! with the PackStream specification, see the [`std_structs`](crate::std_structs) module.
10//! ```
11//! use packs::{Pack, Unpack};
12//! use packs::std_structs::Node;
13//!
14//! let mut node = Node::new(42);
15//! node.properties.add_property("title", "A Book's Title");
16//! node.properties.add_property("pages", 302);
17//!
18//! // encode `node` into a `Vec<u8>`:
19//! let mut buffer = Vec::new();
20//! node.encode(&mut buffer).unwrap();
21//!
22//! // and recover it from these bytes:
23//! let recovered = Node::decode(&mut buffer.as_slice()).unwrap();
24//!
25//! assert_eq!(node, recovered);
26//! ```
27//! # User-Defined Structs
28//! A `struct` can be encoded and decoded in several ways, following the PackStream specification.
29//! Specifying a `#[tag = u8]` attribute interprets the `struct` as a Structure with provided tag
30//! byte and its fields as fields of a structure. I.e. it would be then treated like a `Point2D` or
31//! a `Node` from the `std_structs`.
32//! ```
33//! use packs::*;
34//!
35//! #[derive(Debug, PartialEq, Pack, Unpack)]
36//! #[tag = 0x0B]
37//! struct Book {
38//!     pub title: String,
39//!     pub pages: i64,
40//! }
41//!
42//! // this is not packed as a `Node`. It is a genuinely user defined struct,
43//! // it will differ in its byte structure to the `Node` above.
44//! let book = Book { title: String::from("A Book's title"), pages: 302 };
45//!
46//! let mut buffer = Vec::new();
47//! book.encode(&mut buffer).unwrap();
48//!
49//! let recovered = Book::decode(&mut buffer.as_slice()).unwrap();
50//!
51//! assert_eq!(book, recovered);
52//! ```
53//! ## Providing a sum type
54//! User defined structs are often sumed up in an `enum` which denotes all possible structs
55//! the protocol should be able to encode and decode. This can be given by deriving `Pack` and `Unpack` for an enum.
56//! The `tag` attribute on the different variants is not optional, but it can differ from the one `tag`
57//! attribute provided to the structs themselves.
58//! ```
59//! use packs::*;
60//!
61//! #[derive(Debug, PartialEq, Pack, Unpack)]
62//! #[tag = 0x0B]
63//! struct Book {
64//!     pub title: String,
65//!     pub pages: i64,
66//! }
67//!
68//! #[derive(Debug, PartialEq, Pack, Unpack)]
69//! #[tag = 0x0C]
70//! struct Person {
71//!     pub name: String,
72//! }
73//!
74//! #[derive(Debug, PartialEq, Pack, Unpack)]
75//! enum MyStruct {
76//!     #[tag = 0x0B]
77//!     Book(Book),
78//!     #[tag = 0x0C]
79//!     Person(Person),
80//! }
81//!
82//! let person = Person { name: String::from("Check Mate") };
83//!
84//! let mut buffer = Vec::new();
85//! person.encode(&mut buffer).unwrap();
86//!
87//! // recover via `MyStruct`:
88//! let my_struct = MyStruct::decode(&mut buffer.as_slice()).unwrap();
89//!
90//! assert_eq!(MyStruct::Person(person), my_struct);
91//! ```
92//! ## Tag consistency
93//! Different tags at an enum variant and at its corresponding struct is possible and can be useful
94//! sometimes, to use the same struct in different settings. It might lead to inconsistency if encoding and
95//! decoding doesn't follow the same path though. For example, encoding a
96//! struct with its `Pack` implementation and then decode it, using an enum implementation of `Unpack`
97//! with a different tag will not work.
98//!
99//! # Runtime-typed values
100//! Besides using the types directly, values can be encoded and decoded through a sum type
101//! [`Value`](crate::value::Value) which allows for decoding of any value without knowing its type
102//! beforehand.
103//! ```
104//! use packs::{Value, Unpack, Pack, NoStruct};
105//! use packs::std_structs::StdStruct;
106//!
107//! let mut buffer = Vec::new();
108//! 42i64.encode(&mut buffer).unwrap();
109//!
110//! let value = <Value<NoStruct>>::decode(&mut buffer.as_slice()).unwrap();
111//!
112//! assert_eq!(Value::Integer(42), value);
113//! ```
114//! The type `Value` is abstracted over possible structures. One can use `NoStruct` to deny any
115//! structures or use `Value<StdStruct>` (c.f. [`StdStruct`](crate::std_structs::StdStruct))
116//! to allow any standard structures as part of `Value`.
117//!
118//! To continue on the example from above, `Value<MyStruct>` could have been used there as well:
119//! ```
120//! # use packs::*;
121//! # #[derive(Debug, PartialEq, Pack, Unpack)]
122//! # #[tag = 0x0B]
123//! # struct Book {
124//! #     pub title: String,
125//! #     pub pages: i64,
126//! # }
127//! # #[derive(Debug, PartialEq, Pack, Unpack)]
128//! # #[tag = 0x0C]
129//! # struct Person {
130//! #     pub name: String,
131//! # }
132//! # #[derive(Debug, PartialEq, Pack, Unpack)]
133//! # enum MyStruct {
134//! #    #[tag = 0x0B]
135//! #    Book(Book),
136//! #    #[tag = 0x0C]
137//! #    Person(Person),
138//! # }
139//! let mut buffer = Vec::new();
140//! let person = Person { name: String::from("Check Mate") };
141//! person
142//!     .encode(&mut buffer)
143//!     .unwrap();
144//!
145//! let runtime_typed = <Value<MyStruct>>::decode(&mut buffer.as_slice()).unwrap();
146//!
147//! assert_eq!(Value::Structure(MyStruct::Person(person)), runtime_typed);
148//! ```
149mod value;
150mod structure;
151mod packable;
152mod error;
153pub mod ll;
154pub mod utils;
155
156#[cfg(feature = "std_structs")]
157pub mod std_structs;
158
159#[cfg(feature = "derive")]
160pub use packs_proc::*;
161
162// Public API:
163pub use packable::{Pack, Unpack};
164pub use error::{EncodeError, DecodeError};
165pub use value::{Value, Extract, ExtractRef, ExtractMut, extract_list_ref, extract_list, extract_list_mut};
166pub use value::bytes::Bytes;
167pub use value::dictionary::Dictionary;
168pub use ll::marker::Marker;
169pub use structure::{GenericStruct, NoStruct};