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
//! A streaming writer for the [Kahon binary format].
//!
//! Kahon is a JSON-shaped binary container designed for random access:
//! arrays and objects are laid out as on-disk B+trees, so a reader can
//! index into a million-element array or look up a key in a million-key
//! object without scanning the document. Values are written as they
//! arrive and containers reference children by back-offset, so writer
//! memory stays bounded by tree depth, not document size.
//!
//! [Kahon binary format]: https://github.com/jankdc/kahon
//!
//! # Quick start
//!
//! ```
//! use kahon::Writer;
//!
//! # fn main() -> Result<(), kahon::WriteError> {
//! let mut buf: Vec<u8> = Vec::new();
//! let mut w = Writer::new(&mut buf);
//!
//! {
//! let mut monster = w.start_object();
//! monster.push_i64("hp", 80)?;
//! monster.push_bool("enraged", true)?;
//!
//! {
//! let mut weapons = monster.start_array("weapons")?;
//! weapons.push_str("fist")?;
//!
//! let mut axe = weapons.start_object();
//! axe.push_str("name", "great axe")?;
//! axe.push_i64("damage", 15)?;
//! // `axe` and `weapons` auto-close on drop.
//! }
//!
//! monster.end()?; // explicit close surfaces errors
//! }
//!
//! w.finish()?; // writes the 12-byte trailer
//! # Ok(())
//! # }
//! ```
//!
//! # Building a document
//!
//! Construction follows a flexbuffer-style builder pattern. Start at a
//! [`Writer`], push exactly one root value (scalar, array, or object),
//! then call [`Writer::finish`] to emit the trailer.
//!
//! - Scalars are pushed via `push_null`, `push_bool`, `push_i64`,
//! `push_u64`, `push_f64`, `push_str`.
//! - Arrays and objects are opened with `start_array` / `start_object`,
//! which return an [`ArrayBuilder`] or [`ObjectBuilder`]. Builders
//! borrow their parent mutably and may be nested freely.
//! - Object keys are passed positionally before the value
//! (`obj.push_i64("hp", 80)`). Duplicate keys within an object's
//! sort window resolve last-wins (the latest push for a given key
//! replaces earlier ones).
//!
//! # Closing builders: `Drop` vs `end`
//!
//! Builders close their container on `Drop`, which is convenient for the
//! happy path. If the close encounters a write error, however, `Drop`
//! has nowhere to surface it — the writer is poisoned and the error is
//! reported on the next operation (or on [`Writer::finish`]).
//!
//! Call [`ArrayBuilder::end`] or [`ObjectBuilder::end`] to close
//! explicitly and propagate errors as a `Result`.
//!
//! # Tuning the layout
//!
//! [`WriterOptions`] selects how B+tree nodes are sized and whether the
//! body is padded for page-cache friendliness:
//!
//! ```
//! use kahon::{BuildPolicy, Writer, WriterOptions};
//!
//! # fn main() -> Result<(), kahon::WriteError> {
//! # let mut sink: Vec<u8> = Vec::new();
//! // Disk-tuned: each B+tree node targets one page, trailer is page-aligned.
//! let opts = WriterOptions {
//! policy: BuildPolicy::disk_aligned(4096),
//! ..Default::default()
//! };
//! let w = Writer::with_options(&mut sink, opts)?;
//! # let _ = w;
//! # Ok(())
//! # }
//! ```
//!
//! The default ([`BuildPolicy::compact`] with fanout 128) produces the
//! tightest output and is best for in-memory or network use.
//! [`BuildPolicy::disk_aligned`] trades a small amount of unreferenced
//! padding for a layout that plays well with the page cache when files
//! are `pread`-ed or memory-mapped.
//!
//! # Sinks
//!
//! Any type implementing [`std::io::Write`] is also a [`Sink`] via a
//! blanket impl, so you can write to a `Vec<u8>`, a `File`, a
//! `BufWriter`, or any other writer without adapters.
//!
//! # Errors
//!
//! All fallible operations return [`WriteError`]. Once an error occurs
//! mid-document the writer is *poisoned*: subsequent operations fail
//! fast with [`WriteError::Poisoned`] rather than producing a malformed
//! file.
pub use ;
pub use WriteError;
pub use Sink;
pub use ;
/// Convenience alias for `std::result::Result<T, WriteError>`.
pub type Result<T> = Result;