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
//! Ritobin text format parser and writer for League Toolkit.
//!
//! This crate provides functionality to parse and write the ritobin text format,
//! which is a human-readable representation of League of Legends bin files.
//!
//! # Quick start
//!
//! Parse ritobin text, build a [`Bin`], and round-trip it back to text:
//!
//! ```rust
//! use ltk_ritobin::{Cst, Print as _};
//!
//! let text = r#"
//! #PROP_text
//! type: string = "PROP"
//! version: u32 = 3
//! linked: list[string] = { }
//! entries: map[hash, embed] = { }
//! "#.trim();
//!
//! let cst = Cst::parse(text);
//! assert!(cst.errors.is_empty());
//!
//! let partial = cst.build_bin(text);
//! assert!(partial.diagnostics.is_empty());
//!
//! // Write back to text
//! let output = partial.bin.print().unwrap();
//!
//! assert_eq!(text, output);
//! ```
//!
//! # Converting `.bin` to ritobin
//!
//! `.bin` files identify fields and types by FNV-1a hashes. Supplying a [`HashProvider`]
//! lets the printer emit the original names instead of `0xdeadbeef` literals:
//!
//! ```no_run
//! use std::io::BufReader;
//! use std::fs::File;
//! use ltk_meta::Bin;
//! use ltk_ritobin::{Print, print::PrintConfig, HashMapProvider};
//!
//! let mut reader = BufReader::new(File::open("data.bin")?);
//! let bin = Bin::from_reader(&mut reader)?;
//!
//! let mut hashes = HashMapProvider::new();
//! hashes.load_from_directory("hashes/"); // loads hashes.bin{entries,fields,hashes,types}.txt
//!
//! let text = bin.print_with_config(PrintConfig::default().with_hashes(hashes))?;
//! std::fs::write("data.rito", text)?;
//! # Ok::<(), Box<dyn std::error::Error>>(())
//! ```
//!
//! # Error reporting
//!
//! For resilient parsing, errors exist as nodes into the concrete syntax tree (cst), which propagate into the [`Node`]s' `errors` field (depending on [`parse::ErrorPropagation`]). This
//! allows for more versatile behaviour with things like pretty-printing technically invalid trees,
//! since parsing will always result in a cst.
//!
//! The same handling of errors is done in the type-checker (when building a [`ltk_meta::Bin`]), to
//! always provide a best effort construction.
//!
//! ```rust
//! use ltk_ritobin::Cst;
//!
//! let text = "test: u32 = 4!!2";
//!
//! // by default uses ErrorPropagation::Move,
//! // so all errors will end up in the root
//! let cst = Cst::parse(text);
//!
//! assert_eq!(cst.errors.len(), 1); // the unexpected "!!" in the value
//! ```
//!
//! `Cst::build_bin` follows the same philosophy: it returns a [`ast::PartialBin`], pairing a
//! best-effort `Bin` with any diagnostics, so type errors don't prevent you from getting a
//! `Bin` back. This matters for editor use cases: between keystrokes a buffer is almost always
//! temporarily invalid, and tooling still needs to render it, navigate it, and report problems
//! with precise spans. Use [`ast::PartialBin::into_result`] where you instead want a `Result` that
//! only succeeds on a clean build.
//!
//! # Patch files
//!
//! A file whose `type` root is `"PTCH"` describes a patch bin. Its `patches` root holds one
//! `patch` embed per record, and its optional `deleted` root lists the objects it deletes.
//! [`Cst::build`] builds either kind of file as a [`ltk_meta::BinFile`], and [`Print`] is
//! implemented for [`ltk_meta::BinOverride`] and [`ltk_meta::BinFile`]:
//!
//! ```rust
//! use ltk_meta::BinFile;
//! use ltk_ritobin::Cst;
//!
//! let text = r#"
//! #PTCH_text
//! type: string = "PTCH"
//! version: u32 = 3
//! linked: list[string] = {}
//! entries: map[hash,embed] = {}
//! patches: map[hash,embed] = {
//! 0x4a47c414 = patch {
//! path: string = "Position.Anchors.Anchor"
//! value: vec2 = { 0, 1 }
//! }
//! }
//! "#;
//!
//! let (file, diagnostics) = Cst::parse(text).build(text);
//! assert!(diagnostics.is_empty());
//!
//! let BinFile::Override(patch) = file else { unreachable!() };
//! assert_eq!(patch.patches.len(), 1);
//! ```
use ;
use Bin;
// The `DebugSpans` derive emits `::ltk_ritobin::span` paths.
extern crate self as ltk_ritobin;
pub use Span;
pub use *;
pub use *;
pub use Cst;
/// Items that `rito!` names through `$crate`. Not part of the public API.
pub use Node;
pub use Print;