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
//! Codecs over the canonical Document model. Ported from
//! `~/dev/omnist/omnist/formats.py`; each format's reader parses text into
//! [`crate::document::Doc`] and its writer projects a `Doc` back to text.
//!
//! Unlike [`crate::oml`] (omnist's own format, always lossless), JSON/YAML/
//! TOML/XML can each fail to hold some value losslessly. Writing is
//! **lenient by default**: the writer adjusts the value and records the
//! change in a [`crate::report::WriteReport`]; `strict = true` raises
//! [`crate::error::WriteError`] (carrying the report) instead. See
//! [`crate::report`].
//!
//! This issue (#16) added the first of the four: [`json`]. Issue #18 added
//! [`yaml`]; issue #20 added [`toml`]; issue #22 adds [`xml`], the last and
//! structurally different one -- see `xml.rs`'s own doc comment.
pub
pub
pub
pub
use crate;
use crateOmnistError;
use crateWriteReport;
/// The (read, write, check) contract every builtin format codec already
/// implements as a naming convention -- expressed once (issue #52).
///
/// `read`/`write`/`check` intentionally match the registry's
/// [`crate::registry::ReadFn`]/[`crate::registry::WriteFn`]/
/// [`crate::registry::CheckFn`] signatures exactly: no `strict`, no
/// `report`, no format-specific options (`write_json`'s `indent`,
/// `write_oml`'s `RawNode`/`indent` shape). Each format's richer public
/// `read_X`/`write_X(doc, ..., strict, report)`/`check_X` functions are
/// unchanged and remain the actual public API -- a `Codec` impl is thin
/// `pub(crate)` plumbing that calls them with the registry's documented
/// defaults (`indent: None`, `strict: false`, no report requested; OML's
/// `Doc::from_raw`/`to_raw` bridging), exactly what the hand-written
/// wrapper closures in `registry::builtins` did before this issue -- see
/// `registry.rs`'s module doc for why those defaults are the right ones
/// and why the registry's signatures are simpler than the public
/// functions'.
///
/// A richer trait sketch (scan/emit split, `strict`/`report`-aware
/// `write`) was considered and rejected: `write_json` needs `strict` to
/// pick lenient vs. strict *content* (NaN/Infinity substitution), not just
/// whether `finish_write` raises, so a `strict`-unaware `emit` provided
/// method would be wrong for JSON specifically and each impl would have to
/// override `write` anyway -- collapsing the supposed savings. This
/// leaner shape captures the actual duplication (the registry's adapter
/// closures) without forcing an artificial decomposition that fights each
/// format's real differences.
pub
/// One position `visit_grouped` reaches, passed to its callback alongside
/// the current path. Kept as a single enum (rather than two separate
/// closures) so callers only need one `&mut` capture of their accumulator
/// (e.g. a `WriteReport`) -- two closures both borrowing the same
/// `&mut WriteReport` for the whole walk don't borrow-check, since both
/// would be alive simultaneously across the recursion.
pub
/// Shared traversal for the two codec scanners built directly over a grouped
/// `Value` tree (`json::collect_leaves`/`check_json`, `yaml::scan_nel`) --
/// see issue #51. Both re-implemented the same recursion and the same
/// same-label-array path-numbering rule; this walker does it once.
///
/// `path` is a single reused buffer: every recursive step pushes its segment
/// (via [`crate::report::push_child_path`]), recurses, then truncates back --
/// so a full walk of an all-valid document allocates a path `String` only
/// when `f` itself decides to keep one (e.g. to store it in a
/// `WriteReport`), never once per edge just to *have* a path available
/// (issue #44).
///
/// `toml.rs::strip_nulls` (which transforms the tree, not merely visits it)
/// and `xml.rs::scan_xml_into` (a different tree type, `RawNode`, not
/// `Value`) don't fit this shape and keep their own recursion -- they still
/// use [`crate::report::child_path`] for the path-numbering rule itself.
pub