Skip to main content

twig_sys/
lib.rs

1//! Low-level FFI bindings for Twig's C ABI, plus the build script that links
2//! the native `libtwig.a`.
3//!
4//! This crate is the `-sys` layer: it exposes the raw `extern "C"` functions,
5//! the `#[repr(C)]` type mirrors, and the status/format constants, and nothing
6//! else. The safe, ergonomic API lives in the `twig-doc` crate, which depends
7//! on this one. Direct use is `unsafe` and unstable across ABI-version bumps —
8//! see [`TWIG_ABI_VERSION`].
9#![allow(non_camel_case_types)]
10
11use std::os::raw::{c_char, c_int};
12
13/// The C ABI contract version this binding is written against; see
14/// `twig_abi_version`. Must match the value baked into the linked library
15/// (asserted at runtime by the `abi_version_matches` test in `lib.rs`).
16pub const TWIG_ABI_VERSION: u32 = 3;
17
18// Freeze the canonical 64-bit layout of every `#[repr(C)]` mirror below so it
19// can never silently drift from the Zig `extern struct` it shadows. These are
20// the same offsets asserted on the Zig side in `src/c_abi.zig`; a change on
21// either side that isn't matched on the other fails to compile. Gated on 64-bit
22// (the offsets are the LP64/LLP64 layout); 32-bit targets pack tighter but the
23// C ABI still keeps both languages in agreement.
24#[cfg(target_pointer_width = "64")]
25const _: () = {
26    use std::mem::offset_of;
27    use std::mem::size_of;
28
29    assert!(size_of::<TwigSpan>() == 16);
30    assert!(offset_of!(TwigSpan, start) == 0);
31    assert!(offset_of!(TwigSpan, end) == 8);
32
33    assert!(size_of::<TwigQueryMatch>() == 56);
34    assert!(offset_of!(TwigQueryMatch, node_id) == 0);
35    assert!(offset_of!(TwigQueryMatch, span) == 8);
36    assert!(offset_of!(TwigQueryMatch, content_span) == 24);
37    assert!(offset_of!(TwigQueryMatch, has_content_span) == 40);
38    assert!(offset_of!(TwigQueryMatch, kind) == 48);
39
40    assert!(size_of::<TwigChange>() == 32);
41    assert!(offset_of!(TwigChange, old_span) == 0);
42    assert!(offset_of!(TwigChange, new_span) == 16);
43
44    assert!(size_of::<TwigFlatNode>() == 136);
45    assert!(offset_of!(TwigFlatNode, id) == 0);
46    assert!(offset_of!(TwigFlatNode, parent) == 4);
47    assert!(offset_of!(TwigFlatNode, first_child) == 8);
48    assert!(offset_of!(TwigFlatNode, next_sibling) == 12);
49    assert!(offset_of!(TwigFlatNode, span) == 16);
50    assert!(offset_of!(TwigFlatNode, content_span) == 32);
51    assert!(offset_of!(TwigFlatNode, has_content_span) == 48);
52    assert!(offset_of!(TwigFlatNode, level) == 52);
53    assert!(offset_of!(TwigFlatNode, kind) == 56);
54    assert!(offset_of!(TwigFlatNode, text_ptr) == 64);
55    assert!(offset_of!(TwigFlatNode, text_len) == 72);
56    assert!(offset_of!(TwigFlatNode, destination_ptr) == 80);
57    assert!(offset_of!(TwigFlatNode, destination_len) == 88);
58    assert!(offset_of!(TwigFlatNode, head) == 96);
59    assert!(offset_of!(TwigFlatNode, alignment) == 100);
60    assert!(offset_of!(TwigFlatNode, name_ptr) == 104);
61    assert!(offset_of!(TwigFlatNode, name_len) == 112);
62    assert!(offset_of!(TwigFlatNode, attrs_ptr) == 120);
63    assert!(offset_of!(TwigFlatNode, attrs_len) == 128);
64
65    assert!(size_of::<TwigKeyVal>() == 32);
66    assert!(offset_of!(TwigKeyVal, key) == 0);
67    assert!(offset_of!(TwigKeyVal, key_len) == 8);
68    assert!(offset_of!(TwigKeyVal, value) == 16);
69    assert!(offset_of!(TwigKeyVal, value_len) == 24);
70};
71
72#[repr(transparent)]
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub struct TwigStatus(pub c_int);
75
76#[allow(dead_code)]
77impl TwigStatus {
78    pub const OK: c_int = 0;
79    pub const INVALID_ARGUMENT: c_int = 1;
80    pub const PARSE_ERROR: c_int = 2;
81    pub const OUT_OF_MEMORY: c_int = 3;
82    pub const UNSUPPORTED_FORMAT: c_int = 4;
83    pub const NOT_FOUND: c_int = 5;
84    pub const AMBIGUOUS: c_int = 6;
85    pub const NOT_EDITABLE: c_int = 7;
86    pub const EDIT_CONFLICT: c_int = 8;
87    pub const UNSAFE_METADATA: c_int = 9;
88    pub const INTERNAL_ERROR: c_int = 255;
89}
90
91#[repr(C)]
92#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub enum TwigFormat {
94    Djot = 1,
95    Markdown = 2,
96    Xml = 3,
97    Html = 4,
98}
99
100/// Markdown extension flags for the `md_flags` bitmask of `twig_parse_ext` and
101/// `twig_editor_create_ext`.
102pub const TWIG_MD_DIRECTIVES: u32 = 1 << 0;
103pub const TWIG_MD_MATH: u32 = 1 << 1;
104pub const TWIG_MD_HTML_ELEMENTS: u32 = 1 << 2;
105
106pub enum TwigDocument {}
107
108pub enum TwigEditor {}
109
110pub enum TwigBuilder {}
111
112/// C ABI mirror of Zig's `TwigKeyVal` — one `(key, value)` attribute pair. Used
113/// on the read path ([`TwigFlatNode::attrs_ptr`], borrowed) and the write path
114/// (`twig_builder_set_attrs`, copied). A NULL `value` is a bare attribute.
115#[repr(C)]
116#[derive(Clone, Copy, Debug)]
117pub struct TwigKeyVal {
118    pub key: *const u8,
119    pub key_len: usize,
120    pub value: *const u8,
121    pub value_len: usize,
122}
123
124/// C ABI mirror of Zig's `TwigSpan` — a byte range `[start, end)`.
125#[repr(C)]
126#[derive(Clone, Copy, Debug, Eq, PartialEq)]
127pub struct TwigSpan {
128    pub start: usize,
129    pub end: usize,
130}
131
132/// C ABI mirror of Zig's `TwigQueryMatch` — one node returned by
133/// `twig_document_query`. `content_span` is only meaningful when
134/// `has_content_span` is non-zero. `kind` is a NUL-terminated node-kind name
135/// in static, library-owned storage (never freed).
136#[repr(C)]
137#[derive(Clone, Copy, Debug)]
138pub struct TwigQueryMatch {
139    pub node_id: u32,
140    pub span: TwigSpan,
141    pub content_span: TwigSpan,
142    pub has_content_span: c_int,
143    pub kind: *const c_char,
144}
145
146/// The sentinel `node_id` for "no such node" in a [`TwigFlatNode`] link field.
147pub const TWIG_NO_NODE: u32 = u32::MAX;
148
149/// C ABI mirror of Zig's `TwigChange` — the byte effect of an edit. `old_span`
150/// is the replaced range in the pre-edit source; `new_span` is the range the
151/// replacement occupies in the post-edit source (same start).
152#[repr(C)]
153#[derive(Clone, Copy, Debug)]
154pub struct TwigChange {
155    pub old_span: TwigSpan,
156    pub new_span: TwigSpan,
157}
158
159/// C ABI mirror of Zig's `TwigFlatNode` — one node of the editor's flat tree
160/// snapshot. Link fields (`parent`/`first_child`/`next_sibling`) are ids or
161/// [`TWIG_NO_NODE`]. `content_span` is meaningful only when `has_content_span`.
162/// `kind` is static, library-owned storage; `text`/`destination` pointers
163/// borrow the current parse's payloads (NULL when the kind carries none).
164/// `head`/`alignment` carry a `row`/`cell` payload, each `-1` for a kind that
165/// has none (see [`TWIG_HEAD_NONE`] / [`TWIG_ALIGN_NONE`]). `name_ptr` is a
166/// generic element's tag name (NULL for every other kind); `attrs_ptr` points at
167/// `attrs_len` [`TwigKeyVal`]s (NULL/0 when the node has no attributes). Both
168/// borrow the current parse's payloads with the same lifetime as `text_ptr`.
169#[repr(C)]
170#[derive(Clone, Copy, Debug)]
171pub struct TwigFlatNode {
172    pub id: u32,
173    pub parent: u32,
174    pub first_child: u32,
175    pub next_sibling: u32,
176    pub span: TwigSpan,
177    pub content_span: TwigSpan,
178    pub has_content_span: c_int,
179    pub level: u32,
180    pub kind: *const c_char,
181    pub text_ptr: *const u8,
182    pub text_len: usize,
183    pub destination_ptr: *const u8,
184    pub destination_len: usize,
185    pub head: c_int,
186    pub alignment: c_int,
187    pub name_ptr: *const u8,
188    pub name_len: usize,
189    pub attrs_ptr: *const TwigKeyVal,
190    pub attrs_len: usize,
191}
192
193/// `TwigFlatNode::head` for a node that is neither a `row` nor a `cell`.
194pub const TWIG_HEAD_NONE: c_int = -1;
195
196/// `TwigFlatNode::alignment` codes; `NONE` means the node isn't a `cell`.
197/// `NONE` is part of the ABI contract even though `Alignment::from_c` folds it
198/// into its catch-all, so spell it out rather than leaving the -1 a mystery.
199#[allow(dead_code)]
200pub const TWIG_ALIGN_NONE: c_int = -1;
201pub const TWIG_ALIGN_DEFAULT: c_int = 0;
202pub const TWIG_ALIGN_LEFT: c_int = 1;
203pub const TWIG_ALIGN_RIGHT: c_int = 2;
204pub const TWIG_ALIGN_CENTER: c_int = 3;
205
206// `op` codes for `twig_editor_table_edit` — mirror of `TwigTableOp` in twig.h.
207pub const TWIG_TABLE_INSERT_ROW: c_int = 0;
208pub const TWIG_TABLE_DELETE_ROW: c_int = 1;
209pub const TWIG_TABLE_INSERT_COLUMN: c_int = 2;
210pub const TWIG_TABLE_DELETE_COLUMN: c_int = 3;
211pub const TWIG_TABLE_SET_ALIGNMENT: c_int = 4;
212pub const TWIG_TABLE_MOVE_ROW: c_int = 5;
213pub const TWIG_TABLE_MOVE_COLUMN: c_int = 6;
214
215unsafe extern "C" {
216    pub fn twig_abi_version() -> u32;
217    pub fn twig_version() -> u32;
218    pub fn twig_version_string() -> *const c_char;
219    pub fn twig_parse(
220        input: *const u8,
221        input_len: usize,
222        format: c_int,
223        out_doc: *mut *mut TwigDocument,
224    ) -> TwigStatus;
225    pub fn twig_parse_ext(
226        input: *const u8,
227        input_len: usize,
228        format: c_int,
229        md_flags: u32,
230        out_doc: *mut *mut TwigDocument,
231    ) -> TwigStatus;
232    pub fn twig_document_destroy(doc: *mut TwigDocument);
233    pub fn twig_document_render_html(
234        doc: *mut TwigDocument,
235        out_ptr: *mut *const u8,
236        out_len: *mut usize,
237    ) -> TwigStatus;
238    pub fn twig_document_serialize(
239        doc: *mut TwigDocument,
240        format: c_int,
241        out_ptr: *mut *const u8,
242        out_len: *mut usize,
243    ) -> TwigStatus;
244    pub fn twig_document_ast_json(
245        doc: *mut TwigDocument,
246        out_ptr: *mut *const u8,
247        out_len: *mut usize,
248    ) -> TwigStatus;
249    pub fn twig_document_query(
250        doc: *mut TwigDocument,
251        selector: *const u8,
252        selector_len: usize,
253        out_ptr: *mut *const TwigQueryMatch,
254        out_len: *mut usize,
255    ) -> TwigStatus;
256
257    pub fn twig_editor_create(
258        input: *const u8,
259        input_len: usize,
260        format: c_int,
261        out_editor: *mut *mut TwigEditor,
262    ) -> TwigStatus;
263    pub fn twig_editor_create_ext(
264        input: *const u8,
265        input_len: usize,
266        format: c_int,
267        md_flags: u32,
268        out_editor: *mut *mut TwigEditor,
269    ) -> TwigStatus;
270    pub fn twig_editor_destroy(editor: *mut TwigEditor);
271    pub fn twig_editor_replace(
272        editor: *mut TwigEditor,
273        locator: *const u8,
274        locator_len: usize,
275        text: *const u8,
276        text_len: usize,
277    ) -> TwigStatus;
278    pub fn twig_editor_replace_content(
279        editor: *mut TwigEditor,
280        locator: *const u8,
281        locator_len: usize,
282        text: *const u8,
283        text_len: usize,
284    ) -> TwigStatus;
285    pub fn twig_editor_insert_before(
286        editor: *mut TwigEditor,
287        locator: *const u8,
288        locator_len: usize,
289        text: *const u8,
290        text_len: usize,
291    ) -> TwigStatus;
292    pub fn twig_editor_insert_after(
293        editor: *mut TwigEditor,
294        locator: *const u8,
295        locator_len: usize,
296        text: *const u8,
297        text_len: usize,
298    ) -> TwigStatus;
299    pub fn twig_editor_insert_child(
300        editor: *mut TwigEditor,
301        locator: *const u8,
302        locator_len: usize,
303        child_index: usize,
304        text: *const u8,
305        text_len: usize,
306    ) -> TwigStatus;
307    pub fn twig_editor_delete(
308        editor: *mut TwigEditor,
309        locator: *const u8,
310        locator_len: usize,
311    ) -> TwigStatus;
312    pub fn twig_editor_delete_smart(
313        editor: *mut TwigEditor,
314        locator: *const u8,
315        locator_len: usize,
316    ) -> TwigStatus;
317    pub fn twig_editor_unwrap(
318        editor: *mut TwigEditor,
319        locator: *const u8,
320        locator_len: usize,
321    ) -> TwigStatus;
322    pub fn twig_editor_filter(
323        editor: *mut TwigEditor,
324        drop: *const u8,
325        drop_len: usize,
326        keep: *const u8,
327        keep_len: usize,
328        unwrap_kept: c_int,
329    ) -> TwigStatus;
330    pub fn twig_editor_source(
331        editor: *mut TwigEditor,
332        out_ptr: *mut *const u8,
333        out_len: *mut usize,
334    ) -> TwigStatus;
335    pub fn twig_editor_ast_json(
336        editor: *mut TwigEditor,
337        out_ptr: *mut *const u8,
338        out_len: *mut usize,
339    ) -> TwigStatus;
340    pub fn twig_editor_query(
341        editor: *mut TwigEditor,
342        selector: *const u8,
343        selector_len: usize,
344        out_ptr: *mut *const TwigQueryMatch,
345        out_len: *mut usize,
346    ) -> TwigStatus;
347    pub fn twig_editor_edit_range(
348        editor: *mut TwigEditor,
349        start: usize,
350        end: usize,
351        text: *const u8,
352        text_len: usize,
353        out_change: *mut TwigChange,
354    ) -> TwigStatus;
355    pub fn twig_editor_last_change(
356        editor: *mut TwigEditor,
357        out_change: *mut TwigChange,
358    ) -> TwigStatus;
359    pub fn twig_editor_undo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
360    pub fn twig_editor_redo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
361    pub fn twig_editor_coalesce_last(editor: *mut TwigEditor) -> TwigStatus;
362    pub fn twig_editor_revision(editor: *mut TwigEditor) -> u64;
363    pub fn twig_editor_dirty_range(editor: *mut TwigEditor, out_span: *mut TwigSpan)
364        -> TwigStatus;
365    pub fn twig_editor_clear_dirty(editor: *mut TwigEditor) -> TwigStatus;
366    pub fn twig_editor_set_caret_blob(
367        editor: *mut TwigEditor,
368        blob_ptr: *const u8,
369        blob_len: usize,
370    ) -> TwigStatus;
371    pub fn twig_editor_caret_blob(
372        editor: *mut TwigEditor,
373        out_ptr: *mut *const u8,
374        out_len: *mut usize,
375    ) -> TwigStatus;
376    pub fn twig_editor_nodes(
377        editor: *mut TwigEditor,
378        out_ptr: *mut *const TwigFlatNode,
379        out_len: *mut usize,
380    ) -> TwigStatus;
381    pub fn twig_editor_child_spans(
382        editor: *mut TwigEditor,
383        node_id: u32,
384        out_ptr: *mut *const TwigQueryMatch,
385        out_len: *mut usize,
386    ) -> TwigStatus;
387    pub fn twig_editor_subtree(
388        editor: *mut TwigEditor,
389        node_id: u32,
390        out_ptr: *mut *const TwigFlatNode,
391        out_len: *mut usize,
392    ) -> TwigStatus;
393    pub fn twig_editor_node_at(
394        editor: *mut TwigEditor,
395        offset: usize,
396        out_match: *mut TwigQueryMatch,
397    ) -> TwigStatus;
398    pub fn twig_editor_nodes_at(
399        editor: *mut TwigEditor,
400        offset: usize,
401        out_ptr: *mut *const TwigQueryMatch,
402        out_len: *mut usize,
403    ) -> TwigStatus;
404    pub fn twig_editor_wrap_range(
405        editor: *mut TwigEditor,
406        start: usize,
407        end: usize,
408        kind: c_int,
409        out_change: *mut TwigChange,
410    ) -> TwigStatus;
411    pub fn twig_editor_toggle_inline(
412        editor: *mut TwigEditor,
413        start: usize,
414        end: usize,
415        kind: c_int,
416        out_change: *mut TwigChange,
417    ) -> TwigStatus;
418    pub fn twig_editor_set_block(
419        editor: *mut TwigEditor,
420        offset: usize,
421        block_kind: c_int,
422        level: u32,
423        out_change: *mut TwigChange,
424    ) -> TwigStatus;
425    pub fn twig_editor_toggle_block_container(
426        editor: *mut TwigEditor,
427        start: usize,
428        end: usize,
429        container_kind: c_int,
430        out_change: *mut TwigChange,
431    ) -> TwigStatus;
432    pub fn twig_editor_renumber_ordered_lists(
433        editor: *mut TwigEditor,
434        offset: usize,
435        out_change: *mut TwigChange,
436    ) -> TwigStatus;
437    pub fn twig_editor_table_edit(
438        editor: *mut TwigEditor,
439        offset: usize,
440        op: c_int,
441        arg: c_int,
442        out_change: *mut TwigChange,
443    ) -> TwigStatus;
444    pub fn twig_editor_insert_link(
445        editor: *mut TwigEditor,
446        start: usize,
447        end: usize,
448        destination: *const u8,
449        destination_len: usize,
450        out_change: *mut TwigChange,
451    ) -> TwigStatus;
452
453    pub fn twig_editor_insert_literal(
454        editor: *mut TwigEditor,
455        offset: usize,
456        text: *const u8,
457        text_len: usize,
458        out_change: *mut TwigChange,
459    ) -> TwigStatus;
460
461    pub fn twig_editor_insert_line_break(
462        editor: *mut TwigEditor,
463        offset: usize,
464        out_change: *mut TwigChange,
465    ) -> TwigStatus;
466
467    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
468    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
469    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32) -> TwigStatus;
470    pub fn twig_builder_add_text(
471        builder: *mut TwigBuilder,
472        kind: c_int,
473        text: *const u8,
474        text_len: usize,
475        out_id: *mut u32,
476    ) -> TwigStatus;
477    pub fn twig_builder_add_heading(builder: *mut TwigBuilder, level: u32, out_id: *mut u32) -> TwigStatus;
478    pub fn twig_builder_add_code_block(
479        builder: *mut TwigBuilder,
480        lang: *const u8,
481        lang_len: usize,
482        has_lang: c_int,
483        text: *const u8,
484        text_len: usize,
485        out_id: *mut u32,
486    ) -> TwigStatus;
487    pub fn twig_builder_add_raw_block(
488        builder: *mut TwigBuilder,
489        format: *const u8,
490        format_len: usize,
491        text: *const u8,
492        text_len: usize,
493        out_id: *mut u32,
494    ) -> TwigStatus;
495    pub fn twig_builder_add_metadata(
496        builder: *mut TwigBuilder,
497        lang: *const u8,
498        lang_len: usize,
499        text: *const u8,
500        text_len: usize,
501        out_id: *mut u32,
502    ) -> TwigStatus;
503    pub fn twig_builder_add_raw_inline(
504        builder: *mut TwigBuilder,
505        format: *const u8,
506        format_len: usize,
507        text: *const u8,
508        text_len: usize,
509        out_id: *mut u32,
510    ) -> TwigStatus;
511    pub fn twig_builder_add_smart_punctuation(
512        builder: *mut TwigBuilder,
513        punct_kind: c_int,
514        text: *const u8,
515        text_len: usize,
516        out_id: *mut u32,
517    ) -> TwigStatus;
518    pub fn twig_builder_add_link(
519        builder: *mut TwigBuilder,
520        destination: *const u8,
521        destination_len: usize,
522        has_destination: c_int,
523        reference: *const u8,
524        reference_len: usize,
525        has_reference: c_int,
526        out_id: *mut u32,
527    ) -> TwigStatus;
528    pub fn twig_builder_add_image(
529        builder: *mut TwigBuilder,
530        destination: *const u8,
531        destination_len: usize,
532        has_destination: c_int,
533        reference: *const u8,
534        reference_len: usize,
535        has_reference: c_int,
536        out_id: *mut u32,
537    ) -> TwigStatus;
538    pub fn twig_builder_add_directive(
539        builder: *mut TwigBuilder,
540        form: c_int,
541        name: *const u8,
542        name_len: usize,
543        out_id: *mut u32,
544    ) -> TwigStatus;
545    pub fn twig_builder_add_element(
546        builder: *mut TwigBuilder,
547        name: *const u8,
548        name_len: usize,
549        out_id: *mut u32,
550    ) -> TwigStatus;
551    pub fn twig_builder_add_processing_instruction(
552        builder: *mut TwigBuilder,
553        target: *const u8,
554        target_len: usize,
555        data: *const u8,
556        data_len: usize,
557        out_id: *mut u32,
558    ) -> TwigStatus;
559    pub fn twig_builder_add_footnote(
560        builder: *mut TwigBuilder,
561        label: *const u8,
562        label_len: usize,
563        out_id: *mut u32,
564    ) -> TwigStatus;
565    pub fn twig_builder_add_reference(
566        builder: *mut TwigBuilder,
567        label: *const u8,
568        label_len: usize,
569        destination: *const u8,
570        destination_len: usize,
571        out_id: *mut u32,
572    ) -> TwigStatus;
573    pub fn twig_builder_add_bullet_list(
574        builder: *mut TwigBuilder,
575        style: c_int,
576        tight: c_int,
577        out_id: *mut u32,
578    ) -> TwigStatus;
579    pub fn twig_builder_add_ordered_list(
580        builder: *mut TwigBuilder,
581        numbering: c_int,
582        delim: c_int,
583        tight: c_int,
584        start: u32,
585        has_start: c_int,
586        out_id: *mut u32,
587    ) -> TwigStatus;
588    pub fn twig_builder_add_task_list(builder: *mut TwigBuilder, tight: c_int, out_id: *mut u32) -> TwigStatus;
589    pub fn twig_builder_add_task_list_item(builder: *mut TwigBuilder, checked: c_int, out_id: *mut u32) -> TwigStatus;
590    pub fn twig_builder_add_row(builder: *mut TwigBuilder, head: c_int, out_id: *mut u32) -> TwigStatus;
591    pub fn twig_builder_add_cell(builder: *mut TwigBuilder, head: c_int, alignment: c_int, out_id: *mut u32) -> TwigStatus;
592    pub fn twig_builder_set_children(
593        builder: *mut TwigBuilder,
594        parent: u32,
595        ids: *const u32,
596        ids_len: usize,
597    ) -> TwigStatus;
598    pub fn twig_builder_set_attrs(
599        builder: *mut TwigBuilder,
600        id: u32,
601        kvs: *const TwigKeyVal,
602        kvs_len: usize,
603    ) -> TwigStatus;
604    pub fn twig_builder_render_html(
605        builder: *mut TwigBuilder,
606        root: u32,
607        out_ptr: *mut *const u8,
608        out_len: *mut usize,
609    ) -> TwigStatus;
610    pub fn twig_builder_serialize(
611        builder: *mut TwigBuilder,
612        root: u32,
613        format: c_int,
614        out_ptr: *mut *const u8,
615        out_len: *mut usize,
616    ) -> TwigStatus;
617    pub fn twig_builder_ast_json(
618        builder: *mut TwigBuilder,
619        root: u32,
620        out_ptr: *mut *const u8,
621        out_len: *mut usize,
622    ) -> TwigStatus;
623    pub fn twig_builder_query(
624        builder: *mut TwigBuilder,
625        root: u32,
626        selector: *const u8,
627        selector_len: usize,
628        out_ptr: *mut *const TwigQueryMatch,
629        out_len: *mut usize,
630    ) -> TwigStatus;
631}