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