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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
//! Structs in, structs out, in the spirit of
//! [Glaze](https://github.com/stephenberry/glaze).
//!
//! Values are read straight into your types and written straight out of them.
//! There is no `Value` enum, no token stream, and no document model: a field's
//! bytes are converted exactly once, into the member that will hold them.
//!
//! ```
//! #[derive(Default, PartialEq, Debug)]
//! struct Config {
//! name: String,
//! port: u16,
//! hosts: Vec<String>,
//! }
//!
//! structio::object!(Config { name, port, hosts });
//!
//! let json = r#"{"name":"api","port":8080,"hosts":["a","b"]}"#;
//! let config: Config = structio::from_str(json).unwrap();
//! assert_eq!(config.port, 8080);
//! assert_eq!(structio::to_string(&config), json);
//! ```
//!
//! # Two formats, one schema
//!
//! [`object!`] declares a struct's field list once. Both formats read and
//! write against it, so the same type round-trips through either without a
//! second declaration:
//!
//! - [`json`] is JSON text.
//! - [`beve`] is [BEVE](https://github.com/stephenberry/beve), a tagged binary
//! format that keeps JSON's shape and self-description while storing numbers
//! as numbers and numeric arrays as contiguous blocks.
//!
//! ```
//! # #[derive(Default, PartialEq, Debug)]
//! # struct Sample { id: u32, values: Vec<f64> }
//! # structio::object!(Sample { id, values });
//! let sample = Sample { id: 7, values: vec![1.5, 2.5, 3.5] };
//!
//! let text = structio::to_string(&sample);
//! let binary = structio::to_beve(&sample);
//!
//! assert_eq!(structio::from_str::<Sample>(&text).unwrap(), sample);
//! assert_eq!(structio::from_beve::<Sample>(&binary).unwrap(), sample);
//! ```
//!
//! # Objects and arrays
//!
//! [`object!`] encodes a struct by key. [`array!`] encodes it by position,
//! for a type whose field names carry nothing a reader does not already have:
//!
//! ```
//! #[derive(Default, PartialEq, Debug)]
//! struct Vec3 { x: f64, y: f64, z: f64 }
//! structio::array!(Vec3 [x, y, z]);
//!
//! let v = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
//! assert_eq!(structio::to_string(&v), "[1,2,3]");
//! ```
//!
//! Nothing is hashed and no keys go on the wire, at the cost of a schema that
//! cannot change shape: an array of the wrong length is an error, where an
//! object can be read with a policy that steps over a field it does not
//! recognize.
//!
//! # Enums
//!
//! An enum's schema is its variant names, and they go on the wire as names
//! rather than as positions. A variant carrying nothing is written as its
//! name; a variant carrying a value is written as an object of one member
//! keyed by that name, and is marked `(_)` in the declaration.
//!
//! ```
//! #[derive(Default, PartialEq, Debug)]
//! enum Shape {
//! #[default]
//! Empty,
//! Sides(u32),
//! }
//! structio::tagged_enum!(Shape { Empty, Sides(_) });
//!
//! assert_eq!(structio::to_string(&Shape::Empty), "\"Empty\"");
//! assert_eq!(structio::to_string(&Shape::Sides(3)), r#"{"Sides":3}"#);
//! assert_eq!(structio::from_str::<Shape>(r#"{"Sides":3}"#).unwrap(), Shape::Sides(3));
//! ```
//!
//! [`unit_enum!`] is the same declaration for an enum whose variants all carry
//! nothing, and will not compile if one of them does, so its wire form is a
//! plain string and stays one.
//!
//! A variant carrying nothing reads back from either form, but one carrying a
//! value has only the object form, and the two macros' pages say what each
//! error means.
//! [docs/enums.md](https://github.com/stephenberry/structio/blob/main/docs/enums.md)
//! is the long form.
//!
//! The crate root re-exports the JSON entry points unqualified, because JSON
//! is what most callers want first. The BEVE ones carry the format in the
//! name at the root ([`to_beve`], [`from_beve`]) and drop it inside the
//! module, so [`beve::to_vec`] and [`to_beve`] are the same function.
//!
//! # Design
//!
//! - **No dependencies.** Standard library only.
//! - **No proc-macros.** [`object!`], [`array!`] and [`tagged_enum!`] are
//! `macro_rules!` macros, so there is no proc-macro crate to build and link
//! for the host before your code can, and nothing extra when
//! cross-compiling. It buys a smaller dependency graph rather than a faster
//! build: expanding a declaration costs about what a derive costs.
//! - **A declaration is checked against its type.** Naming a field the struct
//! does not have has always been an error; leaving one out is now one too,
//! naming the field. End a declaration with `..` where the omission is
//! deliberate.
//! - **Keys are hashed at compile time.** [`KeyMap::build`] runs during const
//! evaluation and picks the cheapest perfect hash that fits your key set,
//! from a single byte comparison up to a full key hash. Both formats look
//! keys up in that one table.
//! - **Reads reuse what you already own.** Parsing into an existing value
//! refills its buffers instead of reallocating them.
//!
//! # What it does not do
//!
//! This is only for statically known types. There is no lazy or generic value
//! type: if you need to reach into arbitrary documents from code, this is the
//! wrong tool.
//!
//! A BEVE document can still be looked into without being decoded whole.
//! [`from_beve_at`] reads the one value a JSON Pointer names and steps over
//! everything else, and [`validate_beve`] checks a document is well formed
//! without decoding any of it. What comes back from the first is still a type
//! you declared.
//!
//! Reading a document you have no type for is a different matter, and
//! [`beve_to_json`] is the one entry point that hands back its *contents*
//! without one: it rewrites a whole BEVE document as JSON in a single walk. See
//! [`transcode`] for what survives the trip and what does not.
//!
//! # Complex numbers and matrices
//!
//! BEVE's core covers what JSON covers; its extensions cover what scientific
//! data needs on top of that. [`Complex`] and [`Matrix`] are those two, as
//! ordinary types that work in both formats:
//!
//! ```
//! use structio::{Complex, Matrix, MatrixLayout};
//!
//! let signal = vec![Complex::new(1.0f64, 2.0), Complex::new(3.0, -4.0)];
//! let bytes = structio::to_beve(&signal);
//! assert_eq!(structio::from_beve::<Vec<Complex<f64>>>(&bytes).unwrap(), signal);
//!
//! let m = Matrix::new(MatrixLayout::RowMajor, vec![2, 3], (0..6i32).collect()).unwrap();
//! assert_eq!(
//! structio::to_string(&m),
//! r#"{"layout":"layout_right","extents":[2,3],"value":[0,1,2,3,4,5]}"#
//! );
//! assert_eq!(structio::from_beve::<Matrix<i32>>(&structio::to_beve(&m)).unwrap(), m);
//! ```
//!
//! A run of complex numbers is one header and one block, so a
//! `Vec<Complex<f64>>` moves in a single copy exactly as a `Vec<f64>` does, and
//! a `Matrix<Complex<f64>>` stores its data that way with no case of its own.
//! See [`ext`].
//!
//! # Options
//!
//! Whether the JSON is indented, whether a member that would be null is
//! written at all, and whether a key nothing claims is refused, are decided at
//! compile time by a [policy type](Options). Every entry point has a `_with`
//! twin that takes one, and the plain one is [`Standard`]:
//!
//! ```
//! use structio::{Pretty, SkipNull, to_string, to_string_with};
//!
//! # #[derive(Default)]
//! # struct Server { port: u16, tls: Option<String> }
//! # structio::object!(Server { port, tls });
//! let server = Server { port: 8080, tls: None };
//!
//! assert_eq!(to_string(&server), r#"{"port":8080,"tls":null}"#);
//! assert_eq!(
//! to_string_with::<Pretty, _>(&server),
//! "{\n \"port\": 8080,\n \"tls\": null\n}"
//! );
//! assert_eq!(to_string_with::<SkipNull, _>(&server), r#"{"port":8080}"#);
//! ```
//!
//! Indentation puts every element of an array on a line of its own, which is
//! not what a run of numbers wants. [`PrettyInlineArrays`] keeps each array on
//! the line it began on and indents everything else:
//!
//! ```
//! use structio::{PrettyInlineArrays, to_string_with};
//!
//! # #[derive(Default)]
//! # struct Sample { id: u32, values: Vec<f64> }
//! # structio::object!(Sample { id, values });
//! let sample = Sample { id: 7, values: vec![1.5, 2.5, 3.5] };
//!
//! assert_eq!(
//! to_string_with::<PrettyInlineArrays, _>(&sample),
//! "{\n \"id\": 7,\n \"values\": [1.5, 2.5, 3.5]\n}"
//! );
//! ```
//!
//! Text that is already JSON takes the same policy from the other side.
//! [`prettify()`] lays out a document that did not come from a `Write` impl, and
//! emits its whitespace through the same writer, so the result is what writing
//! the same data would have produced:
//!
//! ```
//! use structio::{PrettyInlineArrays, json::prettify_with, prettify};
//!
//! assert_eq!(prettify(r#"{"a":[1,2]}"#).unwrap(), "{\n \"a\": [\n 1,\n 2\n ]\n}");
//! assert_eq!(
//! prettify_with::<PrettyInlineArrays>(r#"{"a":[1,2]}"#).unwrap(),
//! "{\n \"a\": [1, 2]\n}"
//! );
//! ```
//!
//! [`minify()`] goes the other way, and has no layout to agree with: it copies
//! the document through and drops the whitespace between its tokens. That needs
//! nothing but the strings located, so it neither reads a value nor checks a
//! bracket, which is what makes it the fastest thing here.
//!
//! ```
//! assert_eq!(structio::minify("{\n \"a\": [1, 2]\n}").unwrap(), r#"{"a":[1,2]}"#);
//! ```
//!
//! Reading takes a policy the same way. It has three settings, and only one
//! of them is on by default: a key that no field claims is an
//! [`ErrorCode::UnknownKey`], which catches a typo or the wrong document
//! rather than passing over it in silence. Ask for [`SkipUnknown`] to read a
//! subset of a larger document. The other way round, a declared field the
//! document leaves out is simply left as the destination had it, since reading
//! is into a value that already exists. Mark a field `#[required]` in the
//! declaration and its absence is an [`ErrorCode::MissingKey`] under every
//! policy, which is what a mixed schema wants; [`RequireKeys`] says the same of
//! every field at once.
//!
//! ```
//! use structio::{ErrorCode, SkipUnknown, from_str, from_str_with};
//!
//! # #[derive(Default, Debug)]
//! # struct Server { port: u16 }
//! # structio::object!(Server { port });
//! let doc = r#"{"port":8080,"debug":true}"#;
//!
//! assert_eq!(from_str::<Server>(doc).unwrap_err().code, ErrorCode::UnknownKey);
//! assert_eq!(from_str_with::<SkipUnknown, Server>(doc).unwrap().port, 8080);
//! ```
//!
//! ```
//! use structio::{ErrorCode, RequireKeys, from_str, from_str_with};
//!
//! # #[derive(Default, Debug)]
//! # struct Server { port: u16, host: String }
//! # structio::object!(Server { port, host });
//! let doc = r#"{"port":8080}"#;
//!
//! assert_eq!(from_str::<Server>(doc).unwrap().host, "");
//! let e = from_str_with::<RequireKeys, Server>(doc).unwrap_err();
//! assert_eq!(e.code, ErrorCode::MissingKey);
//! // A member that is not there has no position of its own, so the offset
//! // names the object and the error names the member.
//! assert_eq!(e.key, Some("host"));
//! ```
//!
//! The third is [`AllowComments`], which reads JSONC: `//` and `/* */`
//! wherever whitespace is allowed, for the documents people edit by hand.
//!
//! The setting you do not ask for costs nothing: a compact writer emits no
//! indentation code at all. Combinations are your own unit struct and an impl,
//! since every constant on [`Options`] has a default. See [`options`].
//!
//! # Streaming
//!
//! Documents that do not fit, or have not fully arrived, go through
//! [`json::stream`] and [`beve::stream`]: [`to_writer`] drains into an
//! [`std::io::Write`], [`Documents`] pulls a sequence of values out of an
//! [`std::io::Read`], and [`Feed`] takes chunks pushed at it. The BEVE
//! counterparts are [`beve::to_writer`], [`beve::Documents`] and
//! [`beve::Feed`], and they hand out the elements of a typed array one at a
//! time as readily as whole records. [`beve_to_json_writer`] drains a transcode
//! into a sink the same way.
pub use ;
pub use ;
pub use KeyMap;
pub use ;
pub use ;
pub use ;
// The BEVE entry points carry their format in the name at the root, where
// they sit beside the unqualified JSON ones, and drop it inside the module.
pub use append as append_beve;
pub use append_aligned as append_beve_aligned;
pub use append_aligned_with as append_beve_aligned_with;
pub use append_with as append_beve_with;
pub use from_reader as from_beve_reader;
pub use from_reader_array as from_beve_reader_array;
pub use from_reader_with as from_beve_reader_with;
pub use from_slice as from_beve;
pub use from_slice_at as from_beve_at;
pub use from_slice_at_with as from_beve_at_with;
pub use from_slice_with as from_beve_with;
pub use read_array_into as read_beve_array_into;
pub use read_into as read_beve_into;
pub use read_into_at as read_beve_into_at;
pub use read_into_at_with as read_beve_into_at_with;
pub use read_into_with as read_beve_into_with;
pub use size as beve_size;
pub use size_after as beve_size_after;
pub use size_after_with as beve_size_after_with;
pub use size_aligned as beve_size_aligned;
pub use size_aligned_after as beve_size_aligned_after;
pub use size_aligned_after_with as beve_size_aligned_after_with;
pub use size_aligned_with as beve_size_aligned_with;
pub use size_with as beve_size_with;
pub use slice_ref as beve_slice_ref;
pub use to_vec as to_beve;
pub use to_vec_aligned as to_beve_aligned;
pub use to_vec_aligned_with as to_beve_aligned_with;
pub use to_vec_with as to_beve_with;
pub use to_writer as to_beve_writer;
pub use to_writer_buffered as to_beve_writer_buffered;
pub use to_writer_buffered_with as to_beve_writer_buffered_with;
pub use to_writer_with as to_beve_writer_with;
pub use validate as validate_beve;
pub use validate_reader as validate_beve_reader;
pub use write_into as write_beve_into;
pub use write_into_with as write_beve_into_with;
// Neither format's module owns this one, since it names both.
pub use ;