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
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
//! Compile-time options, for reading and for writing.
//!
//! Options are a *type*, not a value. [`Options`] carries one associated
//! constant per setting, a policy type implements it, and the parsers and
//! writers are generic over that type. Every setting is therefore a `const`
//! inside the code that consults it, so the branch folds away before the
//! optimizer ever sees it and the unselected behaviour costs no code at all.
//!
//! This is the shape Glaze's `glz::opts` has, arrived at differently. Rust has
//! no const generic parameter of struct type on stable, so `write<opts{...}>`
//! has no direct translation; a trait with defaulted associated constants
//! gets the same zero-cost dispatch, and gets a place to document each
//! setting besides.
//!
//! ```
//! use structio::{Pretty, to_string, to_string_with};
//!
//! # #[derive(Default)]
//! # struct P { x: i32 }
//! # structio::object!(P { x });
//! let p = P { x: 1 };
//! assert_eq!(to_string(&p), "{\"x\":1}");
//! assert_eq!(to_string_with::<Pretty, _>(&p), "{\n \"x\": 1\n}");
//! ```
//!
//! # Writing your own
//!
//! The built-in policies below are the common cases. Any combination is a unit
//! struct and an impl that overrides the constants it cares about; everything
//! left out keeps its default, so a policy written today keeps compiling when
//! a later release adds a setting.
//!
//! ```
//! use structio::Options;
//!
//! /// Indented four spaces, with absent members left out, and tolerant of a
//! /// document written against a newer version of the schema.
//! #[derive(Clone, Copy)]
//! pub struct Config;
//!
//! impl Options for Config {
//! const PRETTY: bool = true;
//! const INDENT: usize = 4;
//! const SKIP_NULL: bool = true;
//! const ERROR_ON_UNKNOWN_KEYS: bool = false;
//! }
//! ```
//!
//! # What it costs
//!
//! Code size, in exchange for speed: the read and write paths are compiled
//! once per policy a program actually uses. One policy costs exactly what no
//! policy parameter cost before it.
//!
//! # Reading and writing
//!
//! One trait covers both directions, so a policy names everything a program
//! does with a document rather than making you carry two. A setting that
//! belongs to one direction is simply ignored by the other:
//! [`PRETTY`](Options::PRETTY) means nothing to a parser, and
//! [`ERROR_ON_UNKNOWN_KEYS`](Options::ERROR_ON_UNKNOWN_KEYS) means nothing to
//! a writer. Every writing entry point has a `_with` twin and so does every
//! reading one; [`json::Documents`](crate::json::Documents) and
//! [`json::Feed`](crate::json::Feed) take theirs from `with_options`, one more
//! link in the builder chain they already have.
/// How a value is read and written.
///
/// Implemented by [`Standard`], [`Pretty`], [`PrettyInlineArrays`],
/// [`SkipNull`], [`SkipUnknown`], [`RequireKeys`] and [`AllowComments`], and
/// by any policy type of your own.
/// Every constant has a default, so an implementation states only what it
/// changes.
///
/// The trait is a marker: no method, no value, and the implementing type is
/// never constructed. It is named in the writer's type
/// ([`json::Writer<'_, O>`](crate::json::Writer)) and read through
/// `O::CONSTANT` at the point of use.
/// `O`'s settings, with the writer counting bytes instead of storing them.
///
/// Private, and deliberately: it is the one policy that makes a writer
/// produce nothing, so the only things that may name it are the `size`
/// entry points in [`beve`](crate::beve).
///
/// **Every constant of [`Options`] must be forwarded**, or a measurement stops
/// describing the document the same policy would write.
///
/// Nothing checks that, and it is worth being exact about why rather than
/// claiming a test covers it. [`SKIP_NULL`](Options::SKIP_NULL) is the only
/// constant the BEVE writer reads at all, so every other policy in the crate
/// produces byte-identical binary and no test can tell a forwarded constant
/// from a dropped one: deleting the other six leaves the suite green. The list
/// is therefore a promise kept by hand, and the thing that would break it is a
/// *new* constant that the BEVE writer consults. The note at the foot of
/// [`Options`] is where that gets caught, because it sits where such a
/// constant would be added.
///
/// `fn() -> O` rather than `O`, matching the writer, so this type's auto
/// traits do not depend on a policy it never holds a value of.
pub >);
/// Compact JSON, every declared member written, every unknown key refused.
///
/// The default everywhere.
;
/// Indented JSON, two spaces per level.
///
/// ```
/// # #[derive(Default)]
/// # struct P { x: i32, y: Vec<i32> }
/// # structio::object!(P { x, y });
/// let out = structio::to_string_with::<structio::Pretty, _>(&P { x: 1, y: vec![2, 3] });
/// assert_eq!(out, "{\n \"x\": 1,\n \"y\": [\n 2,\n 3\n ]\n}");
/// ```
;
/// Indented JSON, with each array kept on one line.
///
/// [`Pretty`] in every other respect: two spaces per level, a space after
/// every colon, one object member per line. An array holds its elements on
/// the line its opening bracket sits on, separated by `, `, which is what
/// keeps a document of numeric data readable instead of a column one value
/// wide. An object inside an array still breaks across lines.
///
/// ```
/// # #[derive(Default)]
/// # struct Sample { id: u32, values: Vec<f64> }
/// # structio::object!(Sample { id, values });
/// let s = Sample { id: 7, values: vec![1.5, 2.5, 3.5] };
/// assert_eq!(
/// structio::to_string_with::<structio::PrettyInlineArrays, _>(&s),
/// "{\n \"id\": 7,\n \"values\": [1.5, 2.5, 3.5]\n}"
/// );
/// ```
;
/// Compact, with null members left out.
///
/// ```
/// # #[derive(Default)]
/// # struct P { x: i32, note: Option<String> }
/// # structio::object!(P { x, note });
/// let p = P { x: 1, note: None };
/// assert_eq!(structio::to_string(&p), "{\"x\":1,\"note\":null}");
/// assert_eq!(structio::to_string_with::<structio::SkipNull, _>(&p), "{\"x\":1}");
/// ```
;
/// Unknown keys stepped over rather than refused.
///
/// The policy for reading a subset of a document you did not define, or one
/// written against a newer version of your schema.
///
/// ```
/// # #[derive(Default, Debug)]
/// # struct Port { port: u16 }
/// # structio::object!(Port { port });
/// use structio::{ErrorCode, SkipUnknown, from_str, from_str_with};
///
/// let doc = r#"{"port":8080,"debug":true}"#;
/// assert_eq!(from_str::<Port>(doc).unwrap_err().code, ErrorCode::UnknownKey);
/// assert_eq!(from_str_with::<SkipUnknown, Port>(doc).unwrap().port, 8080);
/// ```
;
/// Every declared field required to be present, and every unknown key still
/// refused.
///
/// The policy for a document that is meant to carry the whole value rather
/// than a patch over one. It is the exact opposite of [`SkipUnknown`]: that
/// one accepts a document that says more than the schema does, this one
/// refuses a document that says less.
///
/// For a schema where only some members are mandatory, which is most of them,
/// mark those `#[required]` in the declaration and read under the default
/// policy. This is the blunt instrument, and it is the right one only where
/// every member really is mandatory.
///
/// ```
/// # #[derive(Default, Debug)]
/// # struct Point { x: f64, y: f64 }
/// # structio::object!(Point { x, y });
/// use structio::{ErrorCode, RequireKeys, from_str, from_str_with};
///
/// let doc = r#"{"x":1.0}"#;
/// assert_eq!(from_str::<Point>(doc).unwrap().y, 0.0);
/// assert_eq!(
/// from_str_with::<RequireKeys, Point>(doc).unwrap_err().code,
/// ErrorCode::MissingKey
/// );
/// ```
;
/// JSONC: `//` and `/* */` comments accepted wherever whitespace is.
///
/// The policy for a document a person maintains rather than a program emits.
/// Everything else is unchanged, so an unknown key is still refused.
///
/// ```
/// # #[derive(Default, Debug)]
/// # struct Limits { retries: u8, timeout_ms: u32 }
/// # structio::object!(Limits { retries, timeout_ms });
/// use structio::{AllowComments, from_str_with};
///
/// let config = r#"{
/// // Give up after this many.
/// "retries": 3,
/// "timeout_ms": 500 /* per attempt */
/// }"#;
///
/// let limits = from_str_with::<AllowComments, Limits>(config).unwrap();
/// assert_eq!(limits.retries, 3);
/// assert_eq!(limits.timeout_ms, 500);
/// ```
;