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
//! # Marked Binary Object Notation
//!
//! mbon is a binary notation that is inspired by the NBT format.
//!
//! It is formed of a sequence of strongly typed values. Each made up of two
//! parts: a mark which defines the type and size of the data, followed by the
//! data. Marks can be different in size and so a single byte prefix is used to
//! differenciate between types.
//!
//! This format is self-describing which means that it is able to know if the
//! data is not formatted correctly or a different type was stored than what
//! was expected. Another feature of the self-describing nature of the format
//! is that you can skip values in the data without the need to parse the complete
//! item, e.g. A 1GB value can be easily skipped by only reading the mark.
//!
//! ## Usage
//!
//! ### Dumping
//!
//! You can dump binary data using the [dumper::Dumper] struct. You can
//! write values directly or use serde's serialize to write more complex data.
//!
//! ```
//! use mbon::dumper::Dumper;
//!
//! let a = 32;
//! let b = "Hello World";
//! let c = b'a';
//!
//! let mut dumper = Dumper::new();
//! dumper.write_int(a).unwrap();
//! dumper.write(&b).unwrap();
//! dumper.write(&c).unwrap();
//!
//! let output = dumper.writer();
//! assert_eq!(output, b"i\x00\x00\x00\x20s\x00\x00\x00\x0bHello Worldca");
//! ```
//!
//! ### Parsing
//!
//! You can parse binary data using the [parser::Parser] struct. You can
//! parse Value's directly, but it is recommended to use serde to parse data.
//!
//! ```
//! use mbon::parser::Parser;
//! use mbon::data::Value;
//!
//! let data = b"i\x00\x00\x00\x20s\x00\x00\x00\x0bHello Worldca";
//!
//! let mut parser = Parser::from(data);
//!
//! let a = parser.next_value().unwrap();
//! let b: String = parser.next().unwrap();
//! let c: u8 = parser.next().unwrap();
//!
//! if let Value::Int(a) = a {
//! assert_eq!(a, 32);
//! } else {
//! panic!("a should have been an int");
//! }
//!
//! assert_eq!(b, "Hello World");
//! assert_eq!(c, b'a');
//! ```
//!
//! ### Embedded Objects
//!
//! If you are wanting to embed a predefined object inside the format, you can
//! impl [object::ObjectDump]/[object::ObjectParse]. Keep in mind that you will
//! need to call [`write_obj()`][write_obj]/[`next_obj()`][next_obj] to take
//! advantage of it.
//!
//! [write_obj]: dumper::Dumper::write_obj
//! [next_obj]: parser::Parser::next_obj
//!
//! ```
//! use mbon::parser::Parser;
//! use mbon::dumper::Dumper;
//! use mbon::error::Error;
//! use mbon::object::{ObjectDump, ObjectParse};
//!
//! #[derive(Debug, PartialEq, Eq)]
//! struct Foo {
//! a: i32,
//! b: String,
//! c: char,
//! }
//!
//! impl ObjectDump for Foo {
//! type Error = Error;
//!
//! fn dump_object(&self) -> Result<Vec<u8>, Self::Error> {
//! let mut dumper = Dumper::new();
//!
//! dumper.write(&self.a)?;
//! dumper.write(&self.b)?;
//! dumper.write(&self.c)?;
//!
//! Ok(dumper.writer())
//! }
//! }
//!
//! impl ObjectParse for Foo {
//! type Error = Error;
//!
//! fn parse_object(object: &[u8]) -> Result<Self, Self::Error> {
//! let mut parser = Parser::from(object);
//!
//! let a = parser.next()?;
//! let b = parser.next()?;
//! let c = parser.next()?;
//!
//! Ok(Self { a, b, c })
//! }
//! }
//!
//! let foo = Foo { a: 32, b: "Hello World".to_owned(), c: '🫠' };
//! let mut dumper = Dumper::new();
//!
//! dumper.write_obj(&foo).unwrap();
//!
//! let buf = dumper.writer();
//! let mut parser = Parser::from(&buf);
//!
//! let new_foo: Foo = parser.next_obj().unwrap();
//!
//! assert_eq!(foo, new_foo);
//! ```
//!
//! ### Async Implementations
//!
//! If you want to parse data asynchronously, you may want to use the provided
//! wrappers: [async_wrapper::AsyncDumper], [async_wrapper::AsyncParser].
//!
//! > You need to enable the feature `async` to use these implementations.
//!
//! ```
//! # #[cfg(feature = "async")] {
//! # futures::executor::block_on(async {
//! use futures::io::{AsyncWriteExt, Cursor};
//!
//! use mbon::async_wrapper::{AsyncDumper, AsyncParser};
//!
//! let writer = Cursor::new(vec![0u8; 5]);
//! let mut dumper = AsyncDumper::from(writer);
//!
//! dumper.write(&15u32)?;
//! dumper.flush().await?;
//!
//! let mut reader = dumper.writer();
//! reader.set_position(0);
//!
//! let mut parser = AsyncParser::from(reader);
//!
//! let val: u32 = parser.next().await?;
//!
//! assert_eq!(val, 15);
//! # Ok::<(), Box<dyn std::error::Error>>(()) }).unwrap();
//! # }
//! ```
//!