diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Cargo.lock | 729 | ||||
-rw-r--r-- | Cargo.toml | 22 | ||||
-rw-r--r-- | flake.lock | 27 | ||||
-rw-r--r-- | flake.nix | 14 | ||||
-rw-r--r-- | src/build.rs | 451 | ||||
-rw-r--r-- | src/cli.rs | 21 | ||||
-rw-r--r-- | src/config.rs | 59 | ||||
-rw-r--r-- | src/filters.rs | 115 | ||||
-rw-r--r-- | src/main.rs | 93 | ||||
-rw-r--r-- | src/utils.rs | 116 |
11 files changed, 1650 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a8ea10b --- /dev/null +++ b/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | result | ||
2 | result-* | ||
3 | /target | ||
diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5a17968 --- /dev/null +++ b/Cargo.lock | |||
@@ -0,0 +1,729 @@ | |||
1 | # This file is automatically @generated by Cargo. | ||
2 | # It is not intended for manual editing. | ||
3 | version = 3 | ||
4 | |||
5 | [[package]] | ||
6 | name = "addr2line" | ||
7 | version = "0.17.0" | ||
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" | ||
10 | dependencies = [ | ||
11 | "gimli", | ||
12 | ] | ||
13 | |||
14 | [[package]] | ||
15 | name = "adler" | ||
16 | version = "1.0.2" | ||
17 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" | ||
19 | |||
20 | [[package]] | ||
21 | name = "aho-corasick" | ||
22 | version = "0.7.18" | ||
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
24 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" | ||
25 | dependencies = [ | ||
26 | "memchr", | ||
27 | ] | ||
28 | |||
29 | [[package]] | ||
30 | name = "arrayvec" | ||
31 | version = "0.5.2" | ||
32 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
33 | checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" | ||
34 | |||
35 | [[package]] | ||
36 | name = "atty" | ||
37 | version = "0.2.14" | ||
38 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
39 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" | ||
40 | dependencies = [ | ||
41 | "hermit-abi", | ||
42 | "libc", | ||
43 | "winapi", | ||
44 | ] | ||
45 | |||
46 | [[package]] | ||
47 | name = "autocfg" | ||
48 | version = "1.0.1" | ||
49 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
50 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" | ||
51 | |||
52 | [[package]] | ||
53 | name = "backtrace" | ||
54 | version = "0.3.63" | ||
55 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
56 | checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" | ||
57 | dependencies = [ | ||
58 | "addr2line", | ||
59 | "cc", | ||
60 | "cfg-if", | ||
61 | "libc", | ||
62 | "miniz_oxide", | ||
63 | "object", | ||
64 | "rustc-demangle", | ||
65 | ] | ||
66 | |||
67 | [[package]] | ||
68 | name = "bitflags" | ||
69 | version = "1.3.2" | ||
70 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
71 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
72 | |||
73 | [[package]] | ||
74 | name = "cc" | ||
75 | version = "1.0.71" | ||
76 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
77 | checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" | ||
78 | |||
79 | [[package]] | ||
80 | name = "cfg-if" | ||
81 | version = "1.0.0" | ||
82 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
83 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||
84 | |||
85 | [[package]] | ||
86 | name = "clap" | ||
87 | version = "3.0.0-beta.5" | ||
88 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
89 | checksum = "feff3878564edb93745d58cf63e17b63f24142506e7a20c87a5521ed7bfb1d63" | ||
90 | dependencies = [ | ||
91 | "atty", | ||
92 | "bitflags", | ||
93 | "clap_derive", | ||
94 | "indexmap", | ||
95 | "lazy_static", | ||
96 | "os_str_bytes", | ||
97 | "strsim", | ||
98 | "termcolor", | ||
99 | "textwrap", | ||
100 | "unicase", | ||
101 | ] | ||
102 | |||
103 | [[package]] | ||
104 | name = "clap_derive" | ||
105 | version = "3.0.0-beta.5" | ||
106 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
107 | checksum = "8b15c6b4f786ffb6192ffe65a36855bc1fc2444bcd0945ae16748dcd6ed7d0d3" | ||
108 | dependencies = [ | ||
109 | "heck", | ||
110 | "proc-macro-error", | ||
111 | "proc-macro2", | ||
112 | "quote", | ||
113 | "syn", | ||
114 | ] | ||
115 | |||
116 | [[package]] | ||
117 | name = "color-eyre" | ||
118 | version = "0.5.11" | ||
119 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
120 | checksum = "1f1885697ee8a177096d42f158922251a41973117f6d8a234cee94b9509157b7" | ||
121 | dependencies = [ | ||
122 | "backtrace", | ||
123 | "color-spantrace", | ||
124 | "eyre", | ||
125 | "indenter", | ||
126 | "once_cell", | ||
127 | "owo-colors", | ||
128 | "tracing-error", | ||
129 | ] | ||
130 | |||
131 | [[package]] | ||
132 | name = "color-spantrace" | ||
133 | version = "0.1.6" | ||
134 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
135 | checksum = "b6eee477a4a8a72f4addd4de416eb56d54bc307b284d6601bafdee1f4ea462d1" | ||
136 | dependencies = [ | ||
137 | "once_cell", | ||
138 | "owo-colors", | ||
139 | "tracing-core", | ||
140 | "tracing-error", | ||
141 | ] | ||
142 | |||
143 | [[package]] | ||
144 | name = "config" | ||
145 | version = "0.11.0" | ||
146 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
147 | checksum = "1b1b9d958c2b1368a663f05538fc1b5975adce1e19f435acceae987aceeeb369" | ||
148 | dependencies = [ | ||
149 | "lazy_static", | ||
150 | "nom", | ||
151 | "rust-ini", | ||
152 | "serde 1.0.130", | ||
153 | "serde-hjson", | ||
154 | "serde_json", | ||
155 | "toml", | ||
156 | "yaml-rust", | ||
157 | ] | ||
158 | |||
159 | [[package]] | ||
160 | name = "either" | ||
161 | version = "1.6.1" | ||
162 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
163 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" | ||
164 | |||
165 | [[package]] | ||
166 | name = "env_logger" | ||
167 | version = "0.9.0" | ||
168 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
169 | checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" | ||
170 | dependencies = [ | ||
171 | "atty", | ||
172 | "humantime", | ||
173 | "log", | ||
174 | "regex", | ||
175 | "termcolor", | ||
176 | ] | ||
177 | |||
178 | [[package]] | ||
179 | name = "eyre" | ||
180 | version = "0.6.5" | ||
181 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
182 | checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" | ||
183 | dependencies = [ | ||
184 | "indenter", | ||
185 | "once_cell", | ||
186 | ] | ||
187 | |||
188 | [[package]] | ||
189 | name = "gimli" | ||
190 | version = "0.26.1" | ||
191 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
192 | checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" | ||
193 | |||
194 | [[package]] | ||
195 | name = "hashbrown" | ||
196 | version = "0.11.2" | ||
197 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
198 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" | ||
199 | |||
200 | [[package]] | ||
201 | name = "heck" | ||
202 | version = "0.3.3" | ||
203 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
204 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" | ||
205 | dependencies = [ | ||
206 | "unicode-segmentation", | ||
207 | ] | ||
208 | |||
209 | [[package]] | ||
210 | name = "hermit-abi" | ||
211 | version = "0.1.19" | ||
212 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
213 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" | ||
214 | dependencies = [ | ||
215 | "libc", | ||
216 | ] | ||
217 | |||
218 | [[package]] | ||
219 | name = "humantime" | ||
220 | version = "2.1.0" | ||
221 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
222 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" | ||
223 | |||
224 | [[package]] | ||
225 | name = "indenter" | ||
226 | version = "0.3.3" | ||
227 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
228 | checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" | ||
229 | |||
230 | [[package]] | ||
231 | name = "indexmap" | ||
232 | version = "1.7.0" | ||
233 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
234 | checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" | ||
235 | dependencies = [ | ||
236 | "autocfg", | ||
237 | "hashbrown", | ||
238 | ] | ||
239 | |||
240 | [[package]] | ||
241 | name = "itertools" | ||
242 | version = "0.8.2" | ||
243 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
244 | checksum = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" | ||
245 | dependencies = [ | ||
246 | "either", | ||
247 | ] | ||
248 | |||
249 | [[package]] | ||
250 | name = "itoa" | ||
251 | version = "0.4.8" | ||
252 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
253 | checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" | ||
254 | |||
255 | [[package]] | ||
256 | name = "lazy_static" | ||
257 | version = "1.4.0" | ||
258 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
259 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" | ||
260 | |||
261 | [[package]] | ||
262 | name = "lexical-core" | ||
263 | version = "0.7.6" | ||
264 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
265 | checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe" | ||
266 | dependencies = [ | ||
267 | "arrayvec", | ||
268 | "bitflags", | ||
269 | "cfg-if", | ||
270 | "ryu", | ||
271 | "static_assertions", | ||
272 | ] | ||
273 | |||
274 | [[package]] | ||
275 | name = "libc" | ||
276 | version = "0.2.106" | ||
277 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
278 | checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" | ||
279 | |||
280 | [[package]] | ||
281 | name = "linked-hash-map" | ||
282 | version = "0.5.4" | ||
283 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
284 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" | ||
285 | |||
286 | [[package]] | ||
287 | name = "log" | ||
288 | version = "0.4.14" | ||
289 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
290 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" | ||
291 | dependencies = [ | ||
292 | "cfg-if", | ||
293 | ] | ||
294 | |||
295 | [[package]] | ||
296 | name = "memchr" | ||
297 | version = "2.4.1" | ||
298 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
299 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" | ||
300 | |||
301 | [[package]] | ||
302 | name = "miniz_oxide" | ||
303 | version = "0.4.4" | ||
304 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
305 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" | ||
306 | dependencies = [ | ||
307 | "adler", | ||
308 | "autocfg", | ||
309 | ] | ||
310 | |||
311 | [[package]] | ||
312 | name = "nom" | ||
313 | version = "5.1.2" | ||
314 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
315 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" | ||
316 | dependencies = [ | ||
317 | "lexical-core", | ||
318 | "memchr", | ||
319 | "version_check", | ||
320 | ] | ||
321 | |||
322 | [[package]] | ||
323 | name = "num-traits" | ||
324 | version = "0.1.43" | ||
325 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
326 | checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" | ||
327 | dependencies = [ | ||
328 | "num-traits 0.2.14", | ||
329 | ] | ||
330 | |||
331 | [[package]] | ||
332 | name = "num-traits" | ||
333 | version = "0.2.14" | ||
334 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
335 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" | ||
336 | dependencies = [ | ||
337 | "autocfg", | ||
338 | ] | ||
339 | |||
340 | [[package]] | ||
341 | name = "object" | ||
342 | version = "0.27.1" | ||
343 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
344 | checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" | ||
345 | dependencies = [ | ||
346 | "memchr", | ||
347 | ] | ||
348 | |||
349 | [[package]] | ||
350 | name = "once_cell" | ||
351 | version = "1.8.0" | ||
352 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
353 | checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" | ||
354 | |||
355 | [[package]] | ||
356 | name = "os_str_bytes" | ||
357 | version = "4.2.0" | ||
358 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
359 | checksum = "addaa943333a514159c80c97ff4a93306530d965d27e139188283cd13e06a799" | ||
360 | dependencies = [ | ||
361 | "memchr", | ||
362 | ] | ||
363 | |||
364 | [[package]] | ||
365 | name = "owo-colors" | ||
366 | version = "1.3.0" | ||
367 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
368 | checksum = "2386b4ebe91c2f7f51082d4cefa145d030e33a1842a96b12e4885cc3c01f7a55" | ||
369 | |||
370 | [[package]] | ||
371 | name = "pandoc" | ||
372 | version = "0.8.6" | ||
373 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
374 | checksum = "8c5ab1abdd81ed62e852d412d18964925e42c8f154c61e89c6296b1a06b6daeb" | ||
375 | dependencies = [ | ||
376 | "itertools", | ||
377 | ] | ||
378 | |||
379 | [[package]] | ||
380 | name = "pandoc-docbook" | ||
381 | version = "0.1.0" | ||
382 | dependencies = [ | ||
383 | "clap", | ||
384 | "color-eyre", | ||
385 | "config", | ||
386 | "env_logger", | ||
387 | "eyre", | ||
388 | "lazy_static", | ||
389 | "log", | ||
390 | "pandoc", | ||
391 | "pandoc_ast", | ||
392 | "serde 1.0.130", | ||
393 | ] | ||
394 | |||
395 | [[package]] | ||
396 | name = "pandoc_ast" | ||
397 | version = "0.8.0" | ||
398 | source = "git+https://github.com/oli-obk/pandoc-ast?rev=d73a7d0a065f568d60bcee6cff2340f75065273e#d73a7d0a065f568d60bcee6cff2340f75065273e" | ||
399 | dependencies = [ | ||
400 | "serde 1.0.130", | ||
401 | "serde_derive", | ||
402 | "serde_json", | ||
403 | ] | ||
404 | |||
405 | [[package]] | ||
406 | name = "pin-project-lite" | ||
407 | version = "0.2.7" | ||
408 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
409 | checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" | ||
410 | |||
411 | [[package]] | ||
412 | name = "proc-macro-error" | ||
413 | version = "1.0.4" | ||
414 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
415 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" | ||
416 | dependencies = [ | ||
417 | "proc-macro-error-attr", | ||
418 | "proc-macro2", | ||
419 | "quote", | ||
420 | "syn", | ||
421 | "version_check", | ||
422 | ] | ||
423 | |||
424 | [[package]] | ||
425 | name = "proc-macro-error-attr" | ||
426 | version = "1.0.4" | ||
427 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
428 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" | ||
429 | dependencies = [ | ||
430 | "proc-macro2", | ||
431 | "quote", | ||
432 | "version_check", | ||
433 | ] | ||
434 | |||
435 | [[package]] | ||
436 | name = "proc-macro2" | ||
437 | version = "1.0.32" | ||
438 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
439 | checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" | ||
440 | dependencies = [ | ||
441 | "unicode-xid", | ||
442 | ] | ||
443 | |||
444 | [[package]] | ||
445 | name = "quote" | ||
446 | version = "1.0.10" | ||
447 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
448 | checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" | ||
449 | dependencies = [ | ||
450 | "proc-macro2", | ||
451 | ] | ||
452 | |||
453 | [[package]] | ||
454 | name = "regex" | ||
455 | version = "1.5.4" | ||
456 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
457 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" | ||
458 | dependencies = [ | ||
459 | "aho-corasick", | ||
460 | "memchr", | ||
461 | "regex-syntax", | ||
462 | ] | ||
463 | |||
464 | [[package]] | ||
465 | name = "regex-syntax" | ||
466 | version = "0.6.25" | ||
467 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
468 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" | ||
469 | |||
470 | [[package]] | ||
471 | name = "rust-ini" | ||
472 | version = "0.13.0" | ||
473 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
474 | checksum = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" | ||
475 | |||
476 | [[package]] | ||
477 | name = "rustc-demangle" | ||
478 | version = "0.1.21" | ||
479 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
480 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" | ||
481 | |||
482 | [[package]] | ||
483 | name = "ryu" | ||
484 | version = "1.0.5" | ||
485 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
486 | checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" | ||
487 | |||
488 | [[package]] | ||
489 | name = "serde" | ||
490 | version = "0.8.23" | ||
491 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
492 | checksum = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" | ||
493 | |||
494 | [[package]] | ||
495 | name = "serde" | ||
496 | version = "1.0.130" | ||
497 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
498 | checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" | ||
499 | dependencies = [ | ||
500 | "serde_derive", | ||
501 | ] | ||
502 | |||
503 | [[package]] | ||
504 | name = "serde-hjson" | ||
505 | version = "0.9.1" | ||
506 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
507 | checksum = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" | ||
508 | dependencies = [ | ||
509 | "lazy_static", | ||
510 | "num-traits 0.1.43", | ||
511 | "regex", | ||
512 | "serde 0.8.23", | ||
513 | ] | ||
514 | |||
515 | [[package]] | ||
516 | name = "serde_derive" | ||
517 | version = "1.0.130" | ||
518 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
519 | checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" | ||
520 | dependencies = [ | ||
521 | "proc-macro2", | ||
522 | "quote", | ||
523 | "syn", | ||
524 | ] | ||
525 | |||
526 | [[package]] | ||
527 | name = "serde_json" | ||
528 | version = "1.0.69" | ||
529 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
530 | checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" | ||
531 | dependencies = [ | ||
532 | "itoa", | ||
533 | "ryu", | ||
534 | "serde 1.0.130", | ||
535 | ] | ||
536 | |||
537 | [[package]] | ||
538 | name = "sharded-slab" | ||
539 | version = "0.1.4" | ||
540 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
541 | checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" | ||
542 | dependencies = [ | ||
543 | "lazy_static", | ||
544 | ] | ||
545 | |||
546 | [[package]] | ||
547 | name = "static_assertions" | ||
548 | version = "1.1.0" | ||
549 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
550 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" | ||
551 | |||
552 | [[package]] | ||
553 | name = "strsim" | ||
554 | version = "0.10.0" | ||
555 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
556 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | ||
557 | |||
558 | [[package]] | ||
559 | name = "syn" | ||
560 | version = "1.0.81" | ||
561 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
562 | checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966" | ||
563 | dependencies = [ | ||
564 | "proc-macro2", | ||
565 | "quote", | ||
566 | "unicode-xid", | ||
567 | ] | ||
568 | |||
569 | [[package]] | ||
570 | name = "termcolor" | ||
571 | version = "1.1.2" | ||
572 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
573 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" | ||
574 | dependencies = [ | ||
575 | "winapi-util", | ||
576 | ] | ||
577 | |||
578 | [[package]] | ||
579 | name = "textwrap" | ||
580 | version = "0.14.2" | ||
581 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
582 | checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80" | ||
583 | dependencies = [ | ||
584 | "unicode-width", | ||
585 | ] | ||
586 | |||
587 | [[package]] | ||
588 | name = "thread_local" | ||
589 | version = "1.1.3" | ||
590 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
591 | checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" | ||
592 | dependencies = [ | ||
593 | "once_cell", | ||
594 | ] | ||
595 | |||
596 | [[package]] | ||
597 | name = "toml" | ||
598 | version = "0.5.8" | ||
599 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
600 | checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" | ||
601 | dependencies = [ | ||
602 | "serde 1.0.130", | ||
603 | ] | ||
604 | |||
605 | [[package]] | ||
606 | name = "tracing" | ||
607 | version = "0.1.29" | ||
608 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
609 | checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" | ||
610 | dependencies = [ | ||
611 | "cfg-if", | ||
612 | "pin-project-lite", | ||
613 | "tracing-attributes", | ||
614 | "tracing-core", | ||
615 | ] | ||
616 | |||
617 | [[package]] | ||
618 | name = "tracing-attributes" | ||
619 | version = "0.1.18" | ||
620 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
621 | checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" | ||
622 | dependencies = [ | ||
623 | "proc-macro2", | ||
624 | "quote", | ||
625 | "syn", | ||
626 | ] | ||
627 | |||
628 | [[package]] | ||
629 | name = "tracing-core" | ||
630 | version = "0.1.21" | ||
631 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
632 | checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" | ||
633 | dependencies = [ | ||
634 | "lazy_static", | ||
635 | ] | ||
636 | |||
637 | [[package]] | ||
638 | name = "tracing-error" | ||
639 | version = "0.1.2" | ||
640 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
641 | checksum = "b4d7c0b83d4a500748fa5879461652b361edf5c9d51ede2a2ac03875ca185e24" | ||
642 | dependencies = [ | ||
643 | "tracing", | ||
644 | "tracing-subscriber", | ||
645 | ] | ||
646 | |||
647 | [[package]] | ||
648 | name = "tracing-subscriber" | ||
649 | version = "0.2.25" | ||
650 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
651 | checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" | ||
652 | dependencies = [ | ||
653 | "sharded-slab", | ||
654 | "thread_local", | ||
655 | "tracing-core", | ||
656 | ] | ||
657 | |||
658 | [[package]] | ||
659 | name = "unicase" | ||
660 | version = "2.6.0" | ||
661 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
662 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" | ||
663 | dependencies = [ | ||
664 | "version_check", | ||
665 | ] | ||
666 | |||
667 | [[package]] | ||
668 | name = "unicode-segmentation" | ||
669 | version = "1.8.0" | ||
670 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
671 | checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" | ||
672 | |||
673 | [[package]] | ||
674 | name = "unicode-width" | ||
675 | version = "0.1.9" | ||
676 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
677 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" | ||
678 | |||
679 | [[package]] | ||
680 | name = "unicode-xid" | ||
681 | version = "0.2.2" | ||
682 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
683 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" | ||
684 | |||
685 | [[package]] | ||
686 | name = "version_check" | ||
687 | version = "0.9.3" | ||
688 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
689 | checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" | ||
690 | |||
691 | [[package]] | ||
692 | name = "winapi" | ||
693 | version = "0.3.9" | ||
694 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
695 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" | ||
696 | dependencies = [ | ||
697 | "winapi-i686-pc-windows-gnu", | ||
698 | "winapi-x86_64-pc-windows-gnu", | ||
699 | ] | ||
700 | |||
701 | [[package]] | ||
702 | name = "winapi-i686-pc-windows-gnu" | ||
703 | version = "0.4.0" | ||
704 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
705 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" | ||
706 | |||
707 | [[package]] | ||
708 | name = "winapi-util" | ||
709 | version = "0.1.5" | ||
710 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
711 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" | ||
712 | dependencies = [ | ||
713 | "winapi", | ||
714 | ] | ||
715 | |||
716 | [[package]] | ||
717 | name = "winapi-x86_64-pc-windows-gnu" | ||
718 | version = "0.4.0" | ||
719 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
720 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | ||
721 | |||
722 | [[package]] | ||
723 | name = "yaml-rust" | ||
724 | version = "0.4.5" | ||
725 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
726 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" | ||
727 | dependencies = [ | ||
728 | "linked-hash-map", | ||
729 | ] | ||
diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..365b07b --- /dev/null +++ b/Cargo.toml | |||
@@ -0,0 +1,22 @@ | |||
1 | [package] | ||
2 | name = "pandoc-docbook" | ||
3 | version = "0.1.0" | ||
4 | edition = "2021" | ||
5 | repository = "https://github.com/minijackson/pandoc-docbook" | ||
6 | |||
7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
8 | |||
9 | [dependencies] | ||
10 | clap = "3.0.0-beta.5" | ||
11 | color-eyre = "0.5" | ||
12 | config = { version = "0.11", features = [ "toml" ] } | ||
13 | env_logger = "0.9" | ||
14 | eyre = "0.6" | ||
15 | lazy_static = "1" | ||
16 | log = "0.4" | ||
17 | pandoc = "0.8" | ||
18 | #pandoc_ast = "0.8" | ||
19 | # Needed: https://github.com/oli-obk/pandoc-ast/pull/12 | ||
20 | # TODO: change this when new version is released | ||
21 | pandoc_ast = { git = "https://github.com/oli-obk/pandoc-ast", rev = "d73a7d0a065f568d60bcee6cff2340f75065273e" } | ||
22 | serde = { version = "1", features = [ "derive" ] } | ||
diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..39b7417 --- /dev/null +++ b/flake.lock | |||
@@ -0,0 +1,27 @@ | |||
1 | { | ||
2 | "nodes": { | ||
3 | "nixpkgs": { | ||
4 | "locked": { | ||
5 | "lastModified": 1636138231, | ||
6 | "narHash": "sha256-FZQ5wBcB9zI21uQMB/PaEQ/cLYk+Bwnig5cg1+2rPgg=", | ||
7 | "owner": "NixOS", | ||
8 | "repo": "nixpkgs", | ||
9 | "rev": "5c02380de3951d0237807c16eb19873cb3c5f75d", | ||
10 | "type": "github" | ||
11 | }, | ||
12 | "original": { | ||
13 | "owner": "NixOS", | ||
14 | "ref": "nixos-21.05", | ||
15 | "repo": "nixpkgs", | ||
16 | "type": "github" | ||
17 | } | ||
18 | }, | ||
19 | "root": { | ||
20 | "inputs": { | ||
21 | "nixpkgs": "nixpkgs" | ||
22 | } | ||
23 | } | ||
24 | }, | ||
25 | "root": "root", | ||
26 | "version": 7 | ||
27 | } | ||
diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..c917cd5 --- /dev/null +++ b/flake.nix | |||
@@ -0,0 +1,14 @@ | |||
1 | { | ||
2 | description = "A very basic flake"; | ||
3 | |||
4 | inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-21.05"; | ||
5 | |||
6 | outputs = { self, nixpkgs }: let | ||
7 | pkgs = nixpkgs.legacyPackages.x86_64-linux; | ||
8 | in { | ||
9 | |||
10 | devShell.x86_64-linux = pkgs.mkShell { | ||
11 | }; | ||
12 | |||
13 | }; | ||
14 | } | ||
diff --git a/src/build.rs b/src/build.rs new file mode 100644 index 0000000..0b0c646 --- /dev/null +++ b/src/build.rs | |||
@@ -0,0 +1,451 @@ | |||
1 | use std::path::{Path, PathBuf}; | ||
2 | |||
3 | use eyre::{eyre, ContextCompat, Result, WrapErr}; | ||
4 | use log::{debug, error, log_enabled, trace, warn}; | ||
5 | use pandoc_ast::MutVisitor; | ||
6 | |||
7 | use crate::{ | ||
8 | filters, | ||
9 | utils::{AutoIdentifier, PandocMeta, PandocOutputExt}, | ||
10 | }; | ||
11 | |||
12 | pub fn do_build(config: &crate::config::Config) -> Result<()> { | ||
13 | let summary = Summary::try_from_file(&config.book.summary)?; | ||
14 | let source_root = Path::new(&config.book.summary) | ||
15 | .parent() | ||
16 | .expect("Summary has no parent"); | ||
17 | let files = summary.collect_source_files(source_root)?; | ||
18 | |||
19 | let build_dir = Path::new(&config.build.build_dir); | ||
20 | trace!("Creating build directory: '{}'", build_dir.display()); | ||
21 | std::fs::create_dir_all(build_dir).wrap_err_with(|| { | ||
22 | format!( | ||
23 | "Could not create build directory: '{}'", | ||
24 | build_dir.display() | ||
25 | ) | ||
26 | })?; | ||
27 | |||
28 | // Pre-create files so that we know which links to relativize | ||
29 | for SourceFile { path, .. } in &files { | ||
30 | let output_file = build_dir.join(path.with_extension("html")); | ||
31 | |||
32 | let product_dir = build_dir.join(path.parent().expect("Source file has no parent")); | ||
33 | trace!("Creating product directory: '{}'", product_dir.display()); | ||
34 | std::fs::create_dir_all(&product_dir).wrap_err_with(|| { | ||
35 | format!( | ||
36 | "Could not create build output directory: '{}'", | ||
37 | product_dir.display() | ||
38 | ) | ||
39 | })?; | ||
40 | |||
41 | std::fs::OpenOptions::new() | ||
42 | .write(true) | ||
43 | .create(true) | ||
44 | .open(&output_file) | ||
45 | .wrap_err_with(|| { | ||
46 | format!("Failed to create output file: '{}'", output_file.display()) | ||
47 | })?; | ||
48 | } | ||
49 | |||
50 | for SourceFile { path, source } in &files { | ||
51 | let mut pandoc_command = pandoc::new(); | ||
52 | |||
53 | let output_file = build_dir.join(path.with_extension("html")); | ||
54 | |||
55 | debug!("Generating file: '{}'", output_file.display()); | ||
56 | |||
57 | // To be captured in the filter | ||
58 | let config_clone = config.clone(); | ||
59 | let source_dir = path | ||
60 | .parent() | ||
61 | .expect("Source file has no parent") | ||
62 | .to_path_buf(); | ||
63 | let build_dir_clone = build_dir.to_path_buf(); | ||
64 | let summary_clone = summary.source.clone(); | ||
65 | |||
66 | pandoc_command | ||
67 | .set_input(pandoc::InputKind::Pipe(source.to_json())) | ||
68 | .set_input_format(pandoc::InputFormat::Json, vec![]) | ||
69 | .set_output(pandoc::OutputKind::File(output_file)) | ||
70 | .set_output_format(pandoc::OutputFormat::Html5, vec![]) | ||
71 | .add_options(&[pandoc::PandocOption::SelfContained]) | ||
72 | .add_filter(move |source| { | ||
73 | let level = source_dir | ||
74 | .components() | ||
75 | .skip_while(|c| matches!(c, std::path::Component::CurDir)) | ||
76 | .count(); | ||
77 | |||
78 | let mut insert_summary_filter = filters::InsertSummary { | ||
79 | level, | ||
80 | summary: &summary_clone, | ||
81 | }; | ||
82 | |||
83 | let mut relativize_urls_filter = filters::RelativizeUrls { | ||
84 | config: &config_clone, | ||
85 | // TODO: other output formats | ||
86 | extension: "html", | ||
87 | build_dir: &build_dir_clone, | ||
88 | source_dir: &source_dir, | ||
89 | }; | ||
90 | |||
91 | let mut source = pandoc_ast::Pandoc::from_json(&source); | ||
92 | insert_summary_filter.walk_pandoc(&mut source); | ||
93 | relativize_urls_filter.walk_pandoc(&mut source); | ||
94 | source.to_json() | ||
95 | }); | ||
96 | |||
97 | if log_enabled!(log::Level::Trace) { | ||
98 | pandoc_command.set_show_cmdline(true); | ||
99 | } | ||
100 | |||
101 | pandoc_command | ||
102 | .execute() | ||
103 | .wrap_err_with(|| format!("Failed to generate output of: '{}'", path.display()))?; | ||
104 | } | ||
105 | |||
106 | Ok(()) | ||
107 | } | ||
108 | |||
109 | // TODO: move that into generated.rs | ||
110 | fn generate_source( | ||
111 | title: Vec<pandoc_ast::Inline>, | ||
112 | children: Vec<(PandocMeta, PathBuf)>, | ||
113 | level: usize, | ||
114 | ) -> Result<pandoc_ast::Pandoc> { | ||
115 | // TODO: make that text configurable | ||
116 | let mut content = vec![pandoc_ast::Block::Para(vec![pandoc_ast::Inline::Str( | ||
117 | "Here are the articles in this section:".to_string(), | ||
118 | )])]; | ||
119 | |||
120 | for (mut child, file) in children { | ||
121 | let title = match child.remove("title") { | ||
122 | None => { | ||
123 | warn!("Missing title for file: '{}'", file.display()); | ||
124 | vec![pandoc_ast::Inline::Str("Untitled page".to_string())] | ||
125 | } | ||
126 | Some(pandoc_ast::MetaValue::MetaInlines(inlines)) => inlines, | ||
127 | Some(pandoc_ast::MetaValue::MetaString(s)) => { | ||
128 | vec![pandoc_ast::Inline::Str(s)] | ||
129 | } | ||
130 | // TODO: check that other values are actually invalid | ||
131 | _ => { | ||
132 | error!("Invalid value for title"); | ||
133 | vec![pandoc_ast::Inline::Str("Untitled page".to_string())] | ||
134 | } | ||
135 | }; | ||
136 | |||
137 | let link_target = std::iter::repeat(std::path::Component::ParentDir) | ||
138 | .take(level) | ||
139 | .collect::<PathBuf>() | ||
140 | .join(file); | ||
141 | |||
142 | content.push(pandoc_ast::Block::Para(vec![pandoc_ast::Inline::Link( | ||
143 | // TODO: attribute to recognize big links? | ||
144 | (String::new(), vec![], vec![]), | ||
145 | title, | ||
146 | ( | ||
147 | link_target | ||
148 | .to_str() | ||
149 | .expect("Filename contains invalid unicode") | ||
150 | .to_string(), | ||
151 | String::new(), | ||
152 | ), | ||
153 | )])); | ||
154 | } | ||
155 | |||
156 | let mut meta = PandocMeta::new(); | ||
157 | meta.insert( | ||
158 | "title".to_string(), | ||
159 | pandoc_ast::MetaValue::MetaInlines(title), | ||
160 | ); | ||
161 | |||
162 | Ok(pandoc_ast::Pandoc { | ||
163 | meta, | ||
164 | blocks: content, | ||
165 | pandoc_api_version: vec![1, 22], | ||
166 | }) | ||
167 | } | ||
168 | |||
169 | fn list_content(block: &mut pandoc_ast::Block) -> Result<&mut Vec<Vec<pandoc_ast::Block>>> { | ||
170 | match block { | ||
171 | pandoc_ast::Block::OrderedList(_, list) => Ok(list), | ||
172 | pandoc_ast::Block::BulletList(list) => Ok(list), | ||
173 | _ => Err(eyre!("Expected list in summary, found something else")), | ||
174 | } | ||
175 | } | ||
176 | |||
177 | fn try_into_node_vec(vec: &mut Vec<Vec<pandoc_ast::Block>>) -> Result<Vec<Node>> { | ||
178 | vec.iter_mut().map(Node::try_from_vec_block).collect() | ||
179 | } | ||
180 | |||
181 | // TODO: support separators like these: | ||
182 | // --------- | ||
183 | |||
184 | #[derive(Debug)] | ||
185 | pub struct Summary { | ||
186 | source: pandoc_ast::Pandoc, | ||
187 | nodes: Vec<Node>, | ||
188 | } | ||
189 | |||
190 | #[derive(Debug)] | ||
191 | struct SourceFile { | ||
192 | path: PathBuf, | ||
193 | source: pandoc_ast::Pandoc, | ||
194 | } | ||
195 | |||
196 | // TODO: move that into summary.rs | ||
197 | impl Summary { | ||
198 | fn try_from_file(file: &str) -> Result<Self> { | ||
199 | debug!("Parsing summary"); | ||
200 | let mut pandoc_command = pandoc::new(); | ||
201 | pandoc_command | ||
202 | .add_input(file) | ||
203 | .set_output_format(pandoc::OutputFormat::Json, vec![]) | ||
204 | .set_output(pandoc::OutputKind::Pipe); | ||
205 | |||
206 | trace!("Launching pandoc command"); | ||
207 | |||
208 | if log_enabled!(log::Level::Trace) { | ||
209 | pandoc_command.set_show_cmdline(true); | ||
210 | } | ||
211 | |||
212 | let output = pandoc_command | ||
213 | .execute() | ||
214 | .wrap_err("Could not execute pandoc")? | ||
215 | .buffer(); | ||
216 | |||
217 | let document = pandoc_ast::Pandoc::from_json(&output); | ||
218 | |||
219 | let summary: Self = document.try_into()?; | ||
220 | if summary.has_files_missing( | ||
221 | Path::new(file) | ||
222 | .parent() | ||
223 | .expect("Summary file has no parent"), | ||
224 | ) { | ||
225 | return Err(eyre!("Files from the summary are missing, aborting")); | ||
226 | } | ||
227 | |||
228 | Ok(summary) | ||
229 | } | ||
230 | |||
231 | fn has_files_missing(&self, root: &Path) -> bool { | ||
232 | // Do not use `.any()` to prevent short-circuiting, we want to report all missing files | ||
233 | self.nodes.iter().fold(false, |acc, node| { | ||
234 | let missing = node.has_files_missing(root); | ||
235 | acc || missing | ||
236 | }) | ||
237 | } | ||
238 | |||
239 | /// Get a list of source files. | ||
240 | /// | ||
241 | /// If a file is a generated file, generate it and store it in memory. | ||
242 | fn collect_source_files(&self, root: &Path) -> Result<Vec<SourceFile>> { | ||
243 | let mut result = Vec::new(); | ||
244 | |||
245 | for node in &self.nodes { | ||
246 | node.collect_source_files(&mut result, root, Path::new("."), 0)?; | ||
247 | } | ||
248 | |||
249 | Ok(result) | ||
250 | } | ||
251 | } | ||
252 | |||
253 | impl TryFrom<pandoc_ast::Pandoc> for Summary { | ||
254 | type Error = eyre::Error; | ||
255 | |||
256 | fn try_from(mut document: pandoc_ast::Pandoc) -> Result<Self, Self::Error> { | ||
257 | if document.blocks.len() != 1 { | ||
258 | return Err(eyre!("Summary does not contain a single list")); | ||
259 | } | ||
260 | |||
261 | let root = &mut document.blocks[0]; | ||
262 | |||
263 | let list = list_content(root)?; | ||
264 | |||
265 | let nodes = list | ||
266 | .iter_mut() | ||
267 | .map(Node::try_from_vec_block) | ||
268 | .collect::<Result<_>>()?; | ||
269 | |||
270 | Ok(Summary { | ||
271 | source: document, | ||
272 | nodes, | ||
273 | }) | ||
274 | } | ||
275 | } | ||
276 | |||
277 | #[derive(Debug)] | ||
278 | pub enum Node { | ||
279 | Provided { | ||
280 | file: String, | ||
281 | children: Vec<Node>, | ||
282 | }, | ||
283 | Generated { | ||
284 | file: String, | ||
285 | title: Vec<pandoc_ast::Inline>, | ||
286 | children: Vec<Node>, | ||
287 | }, | ||
288 | } | ||
289 | |||
290 | impl Node { | ||
291 | fn children(&self) -> &[Node] { | ||
292 | match self { | ||
293 | Node::Provided { children, .. } => children, | ||
294 | Node::Generated { children, .. } => children, | ||
295 | } | ||
296 | } | ||
297 | |||
298 | fn has_files_missing(&self, root: &Path) -> bool { | ||
299 | if let Node::Provided { file, .. } = self { | ||
300 | if !root.join(file).exists() { | ||
301 | error!("File '{}' specified in summary does not exists", file); | ||
302 | return true; | ||
303 | } | ||
304 | } | ||
305 | |||
306 | // Do not use `.any()` to prevent short-circuiting, we want to report all missing files | ||
307 | self.children().iter().fold(false, |acc, node| { | ||
308 | let missing = node.has_files_missing(root); | ||
309 | acc || missing | ||
310 | }) | ||
311 | } | ||
312 | |||
313 | fn collect_source_files( | ||
314 | &self, | ||
315 | result: &mut Vec<SourceFile>, | ||
316 | root: &Path, | ||
317 | parent: &Path, | ||
318 | level: usize, | ||
319 | ) -> Result<()> { | ||
320 | let new_parent; | ||
321 | let children_; | ||
322 | let path; | ||
323 | let source: Box<dyn FnOnce(_) -> _>; | ||
324 | |||
325 | match self { | ||
326 | Node::Provided { file, children } => { | ||
327 | trace!("Parsing file: '{}'", file); | ||
328 | |||
329 | // TODO: some filters here? not all filters, since we may want to filter generated | ||
330 | // files too | ||
331 | let mut pandoc_command = pandoc::new(); | ||
332 | pandoc_command | ||
333 | .add_input(&root.join(file)) | ||
334 | .set_output(pandoc::OutputKind::Pipe) | ||
335 | .set_output_format(pandoc::OutputFormat::Json, vec![]); | ||
336 | |||
337 | if log_enabled!(log::Level::Trace) { | ||
338 | pandoc_command.set_show_cmdline(true); | ||
339 | } | ||
340 | |||
341 | let raw_source = pandoc_command | ||
342 | .execute() | ||
343 | .wrap_err_with(|| format!("Failed to parse '{}'", file))? | ||
344 | .buffer(); | ||
345 | source = Box::new(move |_| Ok(pandoc_ast::Pandoc::from_json(&raw_source))); | ||
346 | |||
347 | let file = Path::new(&file); | ||
348 | let stem = file.file_stem().expect("No file name"); | ||
349 | let id = | ||
350 | AutoIdentifier::from(stem.to_str().wrap_err("Invalid unicode in file name")?); | ||
351 | |||
352 | path = file.into(); | ||
353 | new_parent = file.parent().expect("Source file has no parent").join(&*id); | ||
354 | children_ = children; | ||
355 | } | ||
356 | |||
357 | Self::Generated { | ||
358 | file, | ||
359 | title, | ||
360 | children, | ||
361 | } => { | ||
362 | trace!("Found file to generate: '{}'", file); | ||
363 | |||
364 | path = file.into(); | ||
365 | |||
366 | source = Box::new(move |direct_children| { | ||
367 | generate_source(title.clone(), direct_children, level) | ||
368 | }); | ||
369 | new_parent = Path::new(file).with_extension(""); | ||
370 | children_ = children; | ||
371 | } | ||
372 | }; | ||
373 | |||
374 | let mut direct_children = Vec::with_capacity(children_.len()); | ||
375 | |||
376 | for child in children_ { | ||
377 | child.collect_source_files(result, root, &new_parent, level + 1)?; | ||
378 | let direct_child = result.last().unwrap(); | ||
379 | direct_children.push((direct_child.source.meta.clone(), direct_child.path.clone())); | ||
380 | } | ||
381 | |||
382 | result.push(SourceFile { | ||
383 | path, | ||
384 | source: source(direct_children)?, | ||
385 | }); | ||
386 | |||
387 | Ok(()) | ||
388 | } | ||
389 | |||
390 | // Wil also modify the block to linkify generated pages | ||
391 | fn try_from_vec_block(value: &mut Vec<pandoc_ast::Block>) -> Result<Self> { | ||
392 | if value.len() != 1 && value.len() != 2 { | ||
393 | // TODO: better error message? | ||
394 | return Err(eyre!("Summary does not contain a single list")); | ||
395 | } | ||
396 | |||
397 | let mut value = value.iter_mut(); | ||
398 | |||
399 | let item = match value.next().unwrap() { | ||
400 | pandoc_ast::Block::Plain(inlines) => inlines, | ||
401 | pandoc_ast::Block::Para(inlines) => inlines, | ||
402 | _ => return Err(eyre!("List item is not a link or plain text")), | ||
403 | }; | ||
404 | |||
405 | if item.is_empty() { | ||
406 | return Err(eyre!("Summary list items cannot be empty")); | ||
407 | } | ||
408 | |||
409 | let children = if let Some(children) = value.next() { | ||
410 | try_into_node_vec(list_content(children)?)? | ||
411 | } else { | ||
412 | vec![] | ||
413 | }; | ||
414 | |||
415 | match &item[0] { | ||
416 | pandoc_ast::Inline::Link(_, _, target) => { | ||
417 | if item.len() != 1 { | ||
418 | return Err(eyre!("Summary list item not a single link or plain text")); | ||
419 | } | ||
420 | |||
421 | let file = target.0.clone(); | ||
422 | |||
423 | Ok(Node::Provided { file, children }) | ||
424 | } | ||
425 | _ => { | ||
426 | let title = item.clone(); | ||
427 | |||
428 | let id = AutoIdentifier::from(title.as_slice()); | ||
429 | |||
430 | // TODO: missing parent | ||
431 | |||
432 | // Move generate page into this pass | ||
433 | //let mut file = parent.join(&*id); | ||
434 | //file.set_extension("md"); | ||
435 | |||
436 | // TODO: Attribute to style them differently | ||
437 | *item = vec![pandoc_ast::Inline::Link( | ||
438 | (String::new(), vec!["generated".to_string()], vec![]), | ||
439 | item.clone(), | ||
440 | (id.0.clone(), String::new()), | ||
441 | )]; | ||
442 | |||
443 | Ok(Node::Generated { | ||
444 | file: id.0, | ||
445 | title, | ||
446 | children, | ||
447 | }) | ||
448 | } | ||
449 | } | ||
450 | } | ||
451 | } | ||
diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..30e771a --- /dev/null +++ b/src/cli.rs | |||
@@ -0,0 +1,21 @@ | |||
1 | use clap::{AppSettings, Parser}; | ||
2 | |||
3 | // TODO: document | ||
4 | |||
5 | #[derive(Debug, Parser)] | ||
6 | #[clap(setting = AppSettings::InferSubcommands)] | ||
7 | pub struct Cli { | ||
8 | #[clap(short, long, default_value = "pdbook.toml")] | ||
9 | pub config: String, | ||
10 | #[clap(short, long)] | ||
11 | pub quiet: bool, | ||
12 | #[clap(short, long, parse(from_occurrences))] | ||
13 | pub verbose: u8, | ||
14 | #[clap(subcommand)] | ||
15 | pub subcommand: SubCommand, | ||
16 | } | ||
17 | |||
18 | #[derive(Debug, Parser)] | ||
19 | pub enum SubCommand { | ||
20 | Build, | ||
21 | } | ||
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..53922b0 --- /dev/null +++ b/src/config.rs | |||
@@ -0,0 +1,59 @@ | |||
1 | use log::debug; | ||
2 | use serde::{Deserialize, Serialize}; | ||
3 | |||
4 | #[derive(Debug, Clone, Deserialize, Serialize)] | ||
5 | pub struct Config { | ||
6 | #[serde(default)] | ||
7 | pub book: BookConfig, | ||
8 | #[serde(default)] | ||
9 | pub build: BuildConfig, | ||
10 | } | ||
11 | |||
12 | #[derive(Debug, Clone, Deserialize, Serialize)] | ||
13 | pub struct BookConfig { | ||
14 | #[serde(default = "default_summary")] | ||
15 | pub summary: String, | ||
16 | } | ||
17 | |||
18 | impl Default for BookConfig { | ||
19 | fn default() -> Self { | ||
20 | Self { | ||
21 | summary: default_summary(), | ||
22 | } | ||
23 | } | ||
24 | } | ||
25 | |||
26 | fn default_summary() -> String { | ||
27 | "src/_summary.md".to_string() | ||
28 | } | ||
29 | |||
30 | #[derive(Debug, Clone, Deserialize, Serialize)] | ||
31 | pub struct BuildConfig { | ||
32 | #[serde(default = "default_build_dir")] | ||
33 | pub build_dir: String, | ||
34 | } | ||
35 | |||
36 | impl Default for BuildConfig { | ||
37 | fn default() -> Self { | ||
38 | Self { | ||
39 | build_dir: default_build_dir(), | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | fn default_build_dir() -> String { | ||
45 | "pdbook".to_string() | ||
46 | } | ||
47 | |||
48 | impl Config { | ||
49 | pub fn new(config_file: &str) -> Result<Self, config::ConfigError> { | ||
50 | let mut s = config::Config::default(); | ||
51 | |||
52 | debug!("Parsing config file: {}", config_file); | ||
53 | s.merge(config::File::with_name(config_file))?; | ||
54 | debug!("Parsing config from environment"); | ||
55 | s.merge(config::Environment::with_prefix("PANDOC_DOCBOOK").separator("_"))?; | ||
56 | |||
57 | s.try_into() | ||
58 | } | ||
59 | } | ||
diff --git a/src/filters.rs b/src/filters.rs new file mode 100644 index 0000000..1b06920 --- /dev/null +++ b/src/filters.rs | |||
@@ -0,0 +1,115 @@ | |||
1 | use std::{collections::HashMap, path::Path}; | ||
2 | |||
3 | use log::trace; | ||
4 | use pandoc_ast::MutVisitor; | ||
5 | |||
6 | // If a link points to `./a/b/c.ext`, and a file in the output directory `pdbook/./a/b/c.html` | ||
7 | // exists, rewrite that link. | ||
8 | pub struct RelativizeUrls<'a> { | ||
9 | pub config: &'a crate::config::Config, | ||
10 | pub extension: &'a str, | ||
11 | pub build_dir: &'a Path, | ||
12 | pub source_dir: &'a Path, | ||
13 | } | ||
14 | |||
15 | impl<'a> pandoc_ast::MutVisitor for RelativizeUrls<'a> { | ||
16 | fn walk_inline(&mut self, inline: &mut pandoc_ast::Inline) { | ||
17 | let link = match inline { | ||
18 | pandoc_ast::Inline::Link(_, _, target) => &mut target.0, | ||
19 | _ => return, | ||
20 | }; | ||
21 | |||
22 | if link.starts_with('#') || link.contains("://") { | ||
23 | return; | ||
24 | } | ||
25 | |||
26 | let link_path = self.source_dir.join(&link); | ||
27 | |||
28 | if link_path.is_absolute() { | ||
29 | return; | ||
30 | } | ||
31 | |||
32 | let mut output_path = self.build_dir.join(&link_path); | ||
33 | if !output_path.set_extension(self.extension) { | ||
34 | return; | ||
35 | } | ||
36 | |||
37 | trace!("Checking output_path: {:?}", output_path); | ||
38 | |||
39 | // TODO: warn when referencing a "markdown or other" file not specified in the summary | ||
40 | if output_path.exists() { | ||
41 | // TODO: relativize from URL root | ||
42 | |||
43 | trace!("Relativizing link '{}'", link_path.display()); | ||
44 | |||
45 | *link = Path::new(link) | ||
46 | .with_extension(&self.extension) | ||
47 | .to_str() | ||
48 | .expect("Path constructed from UTF-8 valid strings in not UTF-8 valid") | ||
49 | .to_string(); | ||
50 | |||
51 | trace!("-> into '{}'", link); | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | // Applied just to the summary | ||
57 | pub struct RelativizeSummary { | ||
58 | level: usize, | ||
59 | } | ||
60 | |||
61 | impl pandoc_ast::MutVisitor for RelativizeSummary { | ||
62 | fn walk_inline(&mut self, inline: &mut pandoc_ast::Inline) { | ||
63 | if self.level == 0 { | ||
64 | return; | ||
65 | } | ||
66 | |||
67 | let link = match inline { | ||
68 | pandoc_ast::Inline::Link(_, _, target) => &mut target.0, | ||
69 | _ => return, | ||
70 | }; | ||
71 | |||
72 | // Assume link is to a managed file | ||
73 | for _ in 0..self.level { | ||
74 | link.insert_str(0, "../"); | ||
75 | } | ||
76 | } | ||
77 | } | ||
78 | |||
79 | pub fn relativize_summary(summary: &pandoc_ast::Pandoc, level: usize) -> pandoc_ast::Pandoc { | ||
80 | use std::sync::RwLock; | ||
81 | |||
82 | lazy_static::lazy_static! { | ||
83 | static ref CACHE: RwLock<HashMap<usize, pandoc_ast::Pandoc>> = RwLock::new(HashMap::new()); | ||
84 | } | ||
85 | |||
86 | CACHE | ||
87 | .write() | ||
88 | .expect("Relativized summary cache poison") | ||
89 | .entry(level) | ||
90 | .or_insert_with(|| { | ||
91 | let mut summary = summary.clone(); | ||
92 | RelativizeSummary { level }.walk_pandoc(&mut summary); | ||
93 | summary | ||
94 | }) | ||
95 | .clone() | ||
96 | } | ||
97 | |||
98 | pub struct InsertSummary<'a> { | ||
99 | pub summary: &'a pandoc_ast::Pandoc, | ||
100 | pub level: usize, | ||
101 | } | ||
102 | |||
103 | impl<'a> pandoc_ast::MutVisitor for InsertSummary<'a> { | ||
104 | fn walk_pandoc(&mut self, pandoc: &mut pandoc_ast::Pandoc) { | ||
105 | let summary = relativize_summary(self.summary, self.level); | ||
106 | |||
107 | pandoc.blocks.insert( | ||
108 | 0, | ||
109 | pandoc_ast::Block::Div( | ||
110 | (String::new(), vec!["summary".to_string()], vec![]), | ||
111 | summary.blocks, | ||
112 | ), | ||
113 | ); | ||
114 | } | ||
115 | } | ||
diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..b5de3bf --- /dev/null +++ b/src/main.rs | |||
@@ -0,0 +1,93 @@ | |||
1 | mod build; | ||
2 | mod cli; | ||
3 | mod config; | ||
4 | mod utils; | ||
5 | mod filters; | ||
6 | |||
7 | use std::path::PathBuf; | ||
8 | |||
9 | use cli::Cli; | ||
10 | |||
11 | use clap::Parser; | ||
12 | use eyre::{Result, WrapErr}; | ||
13 | use log::trace; | ||
14 | |||
15 | fn init_log(quiet: bool, verbosity: u8) { | ||
16 | use log::LevelFilter; | ||
17 | |||
18 | let verbosity = match verbosity { | ||
19 | 0 => LevelFilter::Info, | ||
20 | 1 => LevelFilter::Debug, | ||
21 | _ => LevelFilter::Trace, | ||
22 | }; | ||
23 | |||
24 | let env = env_logger::Env::default().default_filter_or(if quiet { | ||
25 | "off".to_string() | ||
26 | } else { | ||
27 | format!("pandoc_docbook={}", verbosity) | ||
28 | }); | ||
29 | |||
30 | let mut builder = env_logger::Builder::from_env(env); | ||
31 | |||
32 | // Shamelessly taken from pretty_env_logger | ||
33 | builder.format(move |f, record| { | ||
34 | use std::io::Write; | ||
35 | |||
36 | let target = record.target(); | ||
37 | |||
38 | let mut style = f.style(); | ||
39 | let level = colored_level(&mut style, record.level()); | ||
40 | |||
41 | let mut style = f.style(); | ||
42 | let target = style.set_bold(true).value(target); | ||
43 | |||
44 | if verbosity >= LevelFilter::Debug { | ||
45 | writeln!(f, " {} {} > {}", level, target, record.args()) | ||
46 | } else { | ||
47 | writeln!(f, " {} > {}", level, record.args()) | ||
48 | } | ||
49 | }); | ||
50 | |||
51 | builder.init(); | ||
52 | } | ||
53 | |||
54 | fn colored_level<'a>( | ||
55 | style: &'a mut env_logger::fmt::Style, | ||
56 | level: log::Level, | ||
57 | ) -> env_logger::fmt::StyledValue<'a, &'static str> { | ||
58 | use env_logger::fmt::Color; | ||
59 | use log::Level; | ||
60 | |||
61 | match level { | ||
62 | Level::Trace => style.set_color(Color::Magenta).value("TRACE"), | ||
63 | Level::Debug => style.set_color(Color::Blue).value("DEBUG"), | ||
64 | Level::Info => style.set_color(Color::Green).value("INFO "), | ||
65 | Level::Warn => style.set_color(Color::Yellow).value("WARN "), | ||
66 | Level::Error => style.set_color(Color::Red).value("ERROR"), | ||
67 | } | ||
68 | } | ||
69 | |||
70 | fn main() -> Result<()> { | ||
71 | color_eyre::install()?; | ||
72 | |||
73 | let cli = Cli::parse(); | ||
74 | init_log(cli.quiet, cli.verbose); | ||
75 | |||
76 | let config = config::Config::new(&cli.config)?; | ||
77 | trace!("Parsed configuration: {:?}", config); | ||
78 | |||
79 | std::env::set_current_dir( | ||
80 | PathBuf::from(cli.config) | ||
81 | .parent() | ||
82 | .expect("Configuration file has no parent"), | ||
83 | ) | ||
84 | .wrap_err("Could not change current directory to the configuration file's directory")?; | ||
85 | |||
86 | match cli.subcommand { | ||
87 | cli::SubCommand::Build => { | ||
88 | build::do_build(&config)? | ||
89 | } | ||
90 | } | ||
91 | |||
92 | Ok(()) | ||
93 | } | ||
diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..8928cfb --- /dev/null +++ b/src/utils.rs | |||
@@ -0,0 +1,116 @@ | |||
1 | use std::path::PathBuf; | ||
2 | |||
3 | pub fn pandoc_stringify(inlines: &[pandoc_ast::Inline]) -> String { | ||
4 | fn pandoc_stringify_(result: &mut String, inlines: &[pandoc_ast::Inline]) { | ||
5 | for inline in inlines { | ||
6 | match inline { | ||
7 | pandoc_ast::Inline::Str(s) | ||
8 | | pandoc_ast::Inline::Code(_, s) | ||
9 | | pandoc_ast::Inline::Math(_, s) => result.push_str(s), | ||
10 | pandoc_ast::Inline::Emph(inner) | ||
11 | | pandoc_ast::Inline::Underline(inner) | ||
12 | | pandoc_ast::Inline::Strong(inner) | ||
13 | | pandoc_ast::Inline::Strikeout(inner) | ||
14 | | pandoc_ast::Inline::Superscript(inner) | ||
15 | | pandoc_ast::Inline::Subscript(inner) | ||
16 | | pandoc_ast::Inline::SmallCaps(inner) | ||
17 | | pandoc_ast::Inline::Quoted(_, inner) | ||
18 | | pandoc_ast::Inline::Cite(_, inner) | ||
19 | | pandoc_ast::Inline::Link(_, inner, _) | ||
20 | | pandoc_ast::Inline::Image(_, inner, _) | ||
21 | | pandoc_ast::Inline::Span(_, inner) => pandoc_stringify_(result, inner), | ||
22 | pandoc_ast::Inline::Space => result.push(' '), | ||
23 | pandoc_ast::Inline::SoftBreak => todo!(), | ||
24 | pandoc_ast::Inline::LineBreak => todo!(), | ||
25 | pandoc_ast::Inline::RawInline(_, _) => todo!(), | ||
26 | pandoc_ast::Inline::Note(_) => todo!(), | ||
27 | } | ||
28 | } | ||
29 | } | ||
30 | |||
31 | let mut result = String::new(); | ||
32 | pandoc_stringify_(&mut result, inlines); | ||
33 | result | ||
34 | } | ||
35 | |||
36 | /// Follows the algorithm specified in the Pandoc manual[1] | ||
37 | /// | ||
38 | /// [1]: <https://pandoc.org/MANUAL.html#extension-auto_identifiers> | ||
39 | #[derive(Debug)] | ||
40 | pub struct AutoIdentifier(pub String); | ||
41 | |||
42 | impl std::ops::Deref for AutoIdentifier { | ||
43 | type Target = String; | ||
44 | |||
45 | fn deref(&self) -> &Self::Target { | ||
46 | &self.0 | ||
47 | } | ||
48 | } | ||
49 | |||
50 | impl From<AutoIdentifier> for String { | ||
51 | fn from(id: AutoIdentifier) -> Self { | ||
52 | id.0 | ||
53 | } | ||
54 | } | ||
55 | |||
56 | impl From<&[pandoc_ast::Inline]> for AutoIdentifier { | ||
57 | fn from(inlines: &[pandoc_ast::Inline]) -> Self { | ||
58 | let text = pandoc_stringify(inlines); | ||
59 | AutoIdentifier::from(text.as_str()) | ||
60 | } | ||
61 | } | ||
62 | |||
63 | impl From<&str> for AutoIdentifier { | ||
64 | fn from(text: &str) -> Self { | ||
65 | let id = text | ||
66 | .chars() | ||
67 | .skip_while(|ch| !ch.is_alphabetic()) | ||
68 | .filter_map(|ch| { | ||
69 | if !ch.is_ascii_alphanumeric() | ||
70 | && !ch.is_whitespace() | ||
71 | && ch != '_' | ||
72 | && ch != '-' | ||
73 | && ch != '.' | ||
74 | { | ||
75 | return None; | ||
76 | } | ||
77 | |||
78 | if ch.is_whitespace() { | ||
79 | return Some('-'); | ||
80 | } | ||
81 | |||
82 | Some(ch.to_ascii_lowercase()) | ||
83 | }) | ||
84 | .collect(); | ||
85 | |||
86 | AutoIdentifier(id) | ||
87 | } | ||
88 | } | ||
89 | |||
90 | pub trait PandocOutputExt { | ||
91 | fn buffer(self) -> String; | ||
92 | fn file(self) -> PathBuf; | ||
93 | } | ||
94 | |||
95 | impl PandocOutputExt for pandoc::PandocOutput { | ||
96 | fn buffer(self) -> String { | ||
97 | match self { | ||
98 | pandoc::PandocOutput::ToBuffer(buffer) => buffer, | ||
99 | pandoc::PandocOutput::ToBufferRaw(_) => { | ||
100 | panic!("Expected text pandoc output, found binary format") | ||
101 | } | ||
102 | pandoc::PandocOutput::ToFile(_) => { | ||
103 | panic!("Expected buffered pandoc output, found file") | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | fn file(self) -> PathBuf { | ||
109 | match self { | ||
110 | pandoc::PandocOutput::ToFile(file) => file, | ||
111 | _ => panic!("Expected file pandoc output, found buffer"), | ||
112 | } | ||
113 | } | ||
114 | } | ||
115 | |||
116 | pub type PandocMeta = pandoc_ast::Map<String, pandoc_ast::MetaValue>; | ||