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 = 6;
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>() == 168);
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    // In what was `directive_form`'s tail padding: `size_of` is still 144 and
66    // every offset above is unchanged. See the ABI note 5 in twig.h.
67    assert!(offset_of!(TwigFlatNode, container_origin) == 140);
68    // Appended, so every offset above keeps its place; only `size_of` moves
69    // (144 -> 168). See `TWIG_ABI_VERSION` note 6.
70    assert!(offset_of!(TwigFlatNode, marker_span) == 144);
71    assert!(offset_of!(TwigFlatNode, has_marker_span) == 160);
72    // In what was `has_marker_span`'s tail padding: `size_of` stays 168.
73    assert!(offset_of!(TwigFlatNode, checked) == 164);
74
75    assert!(size_of::<TwigKeyVal>() == 32);
76    assert!(offset_of!(TwigKeyVal, key) == 0);
77    assert!(offset_of!(TwigKeyVal, key_len) == 8);
78    assert!(offset_of!(TwigKeyVal, value) == 16);
79    assert!(offset_of!(TwigKeyVal, value_len) == 24);
80};
81
82#[repr(transparent)]
83#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub struct TwigStatus(pub c_int);
85
86#[allow(dead_code)]
87impl TwigStatus {
88    pub const OK: c_int = 0;
89    pub const INVALID_ARGUMENT: c_int = 1;
90    pub const PARSE_ERROR: c_int = 2;
91    pub const OUT_OF_MEMORY: c_int = 3;
92    pub const UNSUPPORTED_FORMAT: c_int = 4;
93    pub const NOT_FOUND: c_int = 5;
94    pub const AMBIGUOUS: c_int = 6;
95    pub const NOT_EDITABLE: c_int = 7;
96    pub const EDIT_CONFLICT: c_int = 8;
97    pub const UNSAFE_METADATA: c_int = 9;
98    /// `twig_language_register` refused the language; its `err_buf` says why.
99    pub const INVALID_LANGUAGE: c_int = 10;
100    pub const INTERNAL_ERROR: c_int = 255;
101}
102
103#[repr(C)]
104#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105pub enum TwigFormat {
106    Djot = 1,
107    Markdown = 2,
108    Xml = 3,
109    Html = 4,
110    /// Parses, renders, serializes and edits. The link, image, footnote and
111    /// table gestures report `TWIG_STATUS_UNSUPPORTED_FORMAT` over an
112    /// AsciiDoc document (their AsciiDoc spellings have a shape the gesture
113    /// algorithms cannot write); every other gesture works. What the parser
114    /// leaves unmodelled survives as literal source text.
115    Asciidoc = 5,
116    /// Strict CommonMark, a dialect of `Markdown`: one parser under a
117    /// different preset, `md_flags` laid over it. Writes as Markdown.
118    Commonmark = 6,
119    /// GitHub-Flavored Markdown, a dialect of `Markdown` the same way.
120    Gfm = 7,
121    /// SVG, a dialect of `Xml`: one parser, one serializer, one spelling
122    /// table, under a code of its own. Writes as XML.
123    Svg = 8,
124}
125
126/// `TWIG_FORMAT_RUNTIME_BASE`: the first format code that names a language
127/// registered at runtime rather than compiled in — handed out by
128/// `twig_language_register`, per process, never pinned. Every [`TwigFormat`]
129/// member is below it, and a code in the range that no registration holds is
130/// refused with `TWIG_STATUS_UNSUPPORTED_FORMAT`.
131pub const TWIG_FORMAT_RUNTIME_BASE: c_int = 4096;
132const _: () = assert!((TwigFormat::Svg as c_int) < TWIG_FORMAT_RUNTIME_BASE);
133
134/// Bumped only when a field of [`TwigLanguageVTable`] changes meaning. 2 is
135/// the table with a call record and a `render` slot; a table of version 1
136/// ([`TwigLanguageVTableV1`]) is still read, as a language that reads and
137/// writes.
138pub const TWIG_LANGUAGE_VTABLE_VERSION: u32 = 2;
139
140/// Mirrors `TwigLanguageCall` in `twig.h`: what one call into a language is
141/// about — the row, the features in force (bit `i` the description's
142/// `features[i]`), and the input.
143#[repr(C)]
144#[derive(Clone, Copy)]
145pub struct TwigLanguageCall {
146    pub row: *const u8,
147    pub row_len: usize,
148    pub features: u32,
149    pub input: *const u8,
150    pub input_len: usize,
151}
152
153/// A language function: `call` in, and on success an allocation of the
154/// host's out through `out`/`out_len`, returning 0. On failure it returns
155/// non-zero and may hand out a UTF-8 message the same way. Twig releases
156/// whatever it hands out with the table's `free`.
157pub type TwigLanguageCallFn = unsafe extern "C" fn(
158    user_data: *mut std::ffi::c_void,
159    call: *const TwigLanguageCall,
160    out: *mut *mut u8,
161    out_len: *mut usize,
162) -> c_int;
163
164/// Mirrors `TwigLanguageVTable` in `twig.h`.
165#[repr(C)]
166#[derive(Clone, Copy)]
167pub struct TwigLanguageVTable {
168    pub version: u32,
169    pub user_data: *mut std::ffi::c_void,
170    /// The `describe` document, JSON.
171    pub description: *const u8,
172    pub description_len: usize,
173    pub parse: Option<TwigLanguageCallFn>,
174    /// `None` for a language that only reads.
175    pub print: Option<TwigLanguageCallFn>,
176    /// `None` for a language whose syntax names no renderers, or names only
177    /// `render_block` and answers it with `print`.
178    pub render: Option<TwigLanguageCallFn>,
179    pub free: Option<unsafe extern "C" fn(user_data: *mut std::ffi::c_void, ptr: *mut u8, len: usize)>,
180}
181
182/// A version-1 language function: `input` in, as [`TwigLanguageCall`]'s.
183pub type TwigLanguageFn = unsafe extern "C" fn(
184    user_data: *mut std::ffi::c_void,
185    row: *const u8,
186    row_len: usize,
187    input: *const u8,
188    input_len: usize,
189    out: *mut *mut u8,
190    out_len: *mut usize,
191) -> c_int;
192
193/// Mirrors `TwigLanguageVTableV1` in `twig.h`: the table of version 1, still
194/// read. Passed to `twig_language_register` as a `*const TwigLanguageVTable`.
195#[repr(C)]
196#[derive(Clone, Copy)]
197pub struct TwigLanguageVTableV1 {
198    pub version: u32,
199    pub user_data: *mut std::ffi::c_void,
200    pub description: *const u8,
201    pub description_len: usize,
202    pub parse: Option<TwigLanguageFn>,
203    pub print: Option<TwigLanguageFn>,
204    pub free: Option<unsafe extern "C" fn(user_data: *mut std::ffi::c_void, ptr: *mut u8, len: usize)>,
205}
206
207/// Opaque: the answering end of the helper wire over a host's table.
208#[repr(C)]
209pub struct TwigServer {
210    _private: [u8; 0],
211}
212
213/// Bumped only when a field of [`TwigTransport`] changes meaning.
214pub const TWIG_TRANSPORT_VERSION: u32 = 1;
215
216/// Mirrors `TwigTransport` in `twig.h`: one line of the helper wire traded
217/// for another. The library speaks the wire; the host moves the lines.
218#[repr(C)]
219#[derive(Clone, Copy)]
220pub struct TwigTransport {
221    pub version: u32,
222    pub user_data: *mut std::ffi::c_void,
223    pub exchange: Option<
224        unsafe extern "C" fn(
225            user_data: *mut std::ffi::c_void,
226            request: *const u8,
227            request_len: usize,
228            out: *mut *mut u8,
229            out_len: *mut usize,
230        ) -> c_int,
231    >,
232    pub free: Option<unsafe extern "C" fn(user_data: *mut std::ffi::c_void, ptr: *mut u8, len: usize)>,
233}
234
235#[cfg(target_pointer_width = "64")]
236const _: () = {
237    assert!(std::mem::size_of::<TwigTransport>() == 32);
238    assert!(std::mem::offset_of!(TwigTransport, exchange) == 16);
239    assert!(std::mem::size_of::<TwigLanguageVTable>() == 64);
240    assert!(std::mem::offset_of!(TwigLanguageVTable, user_data) == 8);
241    assert!(std::mem::offset_of!(TwigLanguageVTable, parse) == 32);
242    assert!(std::mem::offset_of!(TwigLanguageVTable, render) == 48);
243    assert!(std::mem::offset_of!(TwigLanguageVTable, free) == 56);
244    assert!(std::mem::size_of::<TwigLanguageVTableV1>() == 56);
245    assert!(std::mem::offset_of!(TwigLanguageVTableV1, free) == 48);
246    assert!(std::mem::size_of::<TwigLanguageCall>() == 40);
247    assert!(std::mem::offset_of!(TwigLanguageCall, features) == 16);
248    assert!(std::mem::offset_of!(TwigLanguageCall, input) == 24);
249};
250
251/// Markdown extension flags for the `md_flags` bitmask of `twig_parse_ext` and
252/// `twig_editor_create_ext` — which carries a runtime row's features instead
253/// (`twig_format_feature_bit`).
254pub const TWIG_MD_DIRECTIVES: u32 = 1 << 0;
255pub const TWIG_MD_MATH: u32 = 1 << 1;
256pub const TWIG_MD_HTML_ELEMENTS: u32 = 1 << 2;
257pub const TWIG_MD_HIGHLIGHT: u32 = 1 << 3;
258pub const TWIG_MD_HIGHLIGHT_COLORS: u32 = 1 << 4;
259
260pub enum TwigDocument {}
261
262pub enum TwigEditor {}
263
264pub enum TwigBuilder {}
265
266/// C ABI mirror of Zig's `TwigKeyVal` — one `(key, value)` attribute pair. Used
267/// on the read path ([`TwigFlatNode::attrs_ptr`], borrowed) and the write path
268/// (`twig_builder_set_attrs`, copied). A NULL `value` is a bare attribute.
269#[repr(C)]
270#[derive(Clone, Copy, Debug)]
271pub struct TwigKeyVal {
272    pub key: *const u8,
273    pub key_len: usize,
274    pub value: *const u8,
275    pub value_len: usize,
276}
277
278/// C ABI mirror of Zig's `TwigSpan` — a byte range `[start, end)`.
279#[repr(C)]
280#[derive(Clone, Copy, Debug, Eq, PartialEq)]
281pub struct TwigSpan {
282    pub start: usize,
283    pub end: usize,
284}
285
286/// C ABI mirror of Zig's `TwigQueryMatch` — one node returned by
287/// `twig_document_query`. `content_span` is only meaningful when
288/// `has_content_span` is non-zero. `kind` is a NUL-terminated node-kind name
289/// in static, library-owned storage (never freed).
290#[repr(C)]
291#[derive(Clone, Copy, Debug)]
292pub struct TwigQueryMatch {
293    pub node_id: u32,
294    pub span: TwigSpan,
295    pub content_span: TwigSpan,
296    pub has_content_span: c_int,
297    pub kind: *const c_char,
298}
299
300/// C ABI mirror of Zig's `TwigWarning` — one thing a conversion would silently
301/// lose. `path` is a slash-separated child-index trail from the analyzed root
302/// (empty for the root itself); `kind` is a NUL-terminated node-kind name in
303/// static, library-owned storage. Both borrow, with the lifetime of the
304/// `twig_document_diagnostics` output array they came from.
305#[repr(C)]
306#[derive(Clone, Copy, Debug)]
307pub struct TwigWarning {
308    pub fidelity: c_int,
309    pub path_ptr: *const u8,
310    pub path_len: usize,
311    pub kind: *const c_char,
312}
313
314/// [`TwigWarning::fidelity`] codes. `FAITHFUL` completes the space and is never
315/// the value of a reported warning — a faithful node is not a warning.
316#[allow(dead_code)]
317pub const TWIG_FIDELITY_FAITHFUL: c_int = 0;
318pub const TWIG_FIDELITY_DEGRADED: c_int = 1;
319pub const TWIG_FIDELITY_DROPPED: c_int = 2;
320pub const TWIG_FIDELITY_ATTRS_DEGRADED: c_int = 3;
321pub const TWIG_FIDELITY_ATTRS_DROPPED: c_int = 4;
322
323/// The sentinel `node_id` for "no such node" in a [`TwigFlatNode`] link field.
324pub const TWIG_NO_NODE: u32 = u32::MAX;
325
326/// C ABI mirror of Zig's `TwigChange` — the byte effect of an edit. `old_span`
327/// is the replaced range in the pre-edit source; `new_span` is the range the
328/// replacement occupies in the post-edit source (same start).
329#[repr(C)]
330#[derive(Clone, Copy, Debug)]
331pub struct TwigChange {
332    pub old_span: TwigSpan,
333    pub new_span: TwigSpan,
334}
335
336/// C ABI mirror of Zig's `TwigFlatNode` — one node of the editor's flat tree
337/// snapshot. Link fields (`parent`/`first_child`/`next_sibling`) are ids or
338/// [`TWIG_NO_NODE`]. `content_span` is meaningful only when `has_content_span`.
339/// `kind` is static, library-owned storage; `text`/`destination` pointers
340/// borrow the current parse's payloads (NULL when the kind carries none).
341/// `head`/`alignment` carry a `row`/`cell` payload, each `-1` for a kind that
342/// has none (see [`TWIG_HEAD_NONE`] / [`TWIG_ALIGN_NONE`]). `name_ptr` is a
343/// generic container's name — an HTML/XML tag or a directive's type — and NULL
344/// for every other kind; `directive_form` is a [`TWIG_DIRECTIVE_NONE`]-defaulted
345/// code for which of the three surface forms it took; `container_origin` is a
346/// [`TWIG_CONTAINER_ORIGIN_NONE`]-defaulted code for whether it was WRITTEN as a
347/// tag or a directive (the two questions are different — see the constants);
348/// `attrs_ptr` points at
349/// `attrs_len` [`TwigKeyVal`]s (NULL/0 when the node has no attributes). Both
350/// borrow the current parse's payloads with the same lifetime as `text_ptr`.
351#[repr(C)]
352#[derive(Clone, Copy, Debug)]
353pub struct TwigFlatNode {
354    pub id: u32,
355    pub parent: u32,
356    pub first_child: u32,
357    pub next_sibling: u32,
358    pub span: TwigSpan,
359    pub content_span: TwigSpan,
360    pub has_content_span: c_int,
361    pub level: u32,
362    pub kind: *const c_char,
363    pub text_ptr: *const u8,
364    pub text_len: usize,
365    pub destination_ptr: *const u8,
366    pub destination_len: usize,
367    pub head: c_int,
368    pub alignment: c_int,
369    pub name_ptr: *const u8,
370    pub name_len: usize,
371    pub attrs_ptr: *const TwigKeyVal,
372    pub attrs_len: usize,
373    pub directive_form: c_int,
374    pub container_origin: c_int,
375    pub marker_span: TwigSpan,
376    pub has_marker_span: c_int,
377    pub checked: c_int,
378}
379
380/// `TwigFlatNode::checked` for a node that is not a `task_list_item`.
381pub const TWIG_TASK_CHECKED_NONE: c_int = -1;
382
383/// `TwigFlatNode::head` for a node that is neither a `row` nor a `cell`.
384pub const TWIG_HEAD_NONE: c_int = -1;
385
386/// `TwigFlatNode::alignment` codes; `NONE` means the node isn't a `cell`.
387/// `NONE` is part of the ABI contract even though `Alignment::from_c` folds it
388/// into its catch-all, so spell it out rather than leaving the -1 a mystery.
389#[allow(dead_code)]
390pub const TWIG_ALIGN_NONE: c_int = -1;
391pub const TWIG_ALIGN_DEFAULT: c_int = 0;
392pub const TWIG_ALIGN_LEFT: c_int = 1;
393pub const TWIG_ALIGN_RIGHT: c_int = 2;
394pub const TWIG_ALIGN_CENTER: c_int = 3;
395
396/// `TwigFlatNode::directive_form` codes — the generic-directives proposal's
397/// three spellings: `:name[x]` (text), `::name{…}` (leaf), `:::name{…}` … `:::`
398/// (container). These double as `twig_builder_add_directive`'s `form` argument.
399/// `NONE` means the node isn't a `directive` at all; like [`TWIG_ALIGN_NONE`] it
400/// is a read-path-only code, never something you can build with.
401#[allow(dead_code)]
402pub const TWIG_DIRECTIVE_NONE: c_int = -1;
403pub const TWIG_DIRECTIVE_TEXT: c_int = 0;
404pub const TWIG_DIRECTIVE_LEAF: c_int = 1;
405pub const TWIG_DIRECTIVE_CONTAINER: c_int = 2;
406
407/// `TwigFlatNode::container_origin` codes — whether a generic container was
408/// WRITTEN as a tag or as a directive.
409///
410/// A different question from [`TWIG_DIRECTIVE_TEXT`] and friends, which say
411/// which of the three directive spellings a node's producer draws. HTML's
412/// parser gives `<div>` and `<span>` a `directive_form` (they are the two tags
413/// djot and Markdown have generic spellings for), so `directive_form` cannot
414/// separate an HTML `<div>` from a Markdown `:::div`: those two agree on kind,
415/// on name and on form, field for field. This is the code that separates them.
416///
417/// `NONE` means nothing recorded an origin — the node isn't a container, or no
418/// parser produced it.
419#[allow(dead_code)]
420pub const TWIG_CONTAINER_ORIGIN_NONE: c_int = -1;
421pub const TWIG_CONTAINER_ORIGIN_ELEMENT: c_int = 0;
422pub const TWIG_CONTAINER_ORIGIN_DIRECTIVE: c_int = 1;
423
424// `op` codes for `twig_editor_table_edit` — mirror of `TwigTableOp` in twig.h.
425pub const TWIG_TABLE_INSERT_ROW: c_int = 0;
426pub const TWIG_TABLE_DELETE_ROW: c_int = 1;
427pub const TWIG_TABLE_INSERT_COLUMN: c_int = 2;
428pub const TWIG_TABLE_DELETE_COLUMN: c_int = 3;
429pub const TWIG_TABLE_SET_ALIGNMENT: c_int = 4;
430pub const TWIG_TABLE_MOVE_ROW: c_int = 5;
431pub const TWIG_TABLE_MOVE_COLUMN: c_int = 6;
432
433unsafe extern "C" {
434    pub fn twig_abi_version() -> u32;
435    pub fn twig_language_register(
436        vtable: *const TwigLanguageVTable,
437        out_format: *mut c_int,
438        err_buf: *mut c_char,
439        err_cap: usize,
440    ) -> TwigStatus;
441    pub fn twig_language_register_transport(
442        transport: *const TwigTransport,
443        out_format: *mut c_int,
444        err_buf: *mut c_char,
445        err_cap: usize,
446    ) -> TwigStatus;
447    pub fn twig_format_by_name(name: *const u8, name_len: usize, out_format: *mut c_int) -> TwigStatus;
448    pub fn twig_format_feature_bit(
449        format: c_int,
450        name: *const u8,
451        name_len: usize,
452        out_bit: *mut u32,
453    ) -> TwigStatus;
454    pub fn twig_server_create(
455        vtable: *const TwigLanguageVTable,
456        out_server: *mut *mut TwigServer,
457        err_buf: *mut c_char,
458        err_cap: usize,
459    ) -> TwigStatus;
460    pub fn twig_server_handle(
461        server: *mut TwigServer,
462        request: *const u8,
463        request_len: usize,
464        out_ptr: *mut *const u8,
465        out_len: *mut usize,
466    ) -> TwigStatus;
467    pub fn twig_server_destroy(server: *mut TwigServer);
468    pub fn twig_format_name(format: c_int, out_ptr: *mut *const u8, out_len: *mut usize) -> TwigStatus;
469    pub fn twig_format_dialect_of(format: c_int, out_format: *mut c_int) -> TwigStatus;
470    pub fn twig_version() -> u32;
471    pub fn twig_version_string() -> *const c_char;
472    pub fn twig_parse(
473        input: *const u8,
474        input_len: usize,
475        format: c_int,
476        out_doc: *mut *mut TwigDocument,
477    ) -> TwigStatus;
478    pub fn twig_parse_ext(
479        input: *const u8,
480        input_len: usize,
481        format: c_int,
482        md_flags: u32,
483        out_doc: *mut *mut TwigDocument,
484    ) -> TwigStatus;
485    pub fn twig_document_destroy(doc: *mut TwigDocument);
486    pub fn twig_document_render_html(
487        doc: *mut TwigDocument,
488        out_ptr: *mut *const u8,
489        out_len: *mut usize,
490    ) -> TwigStatus;
491    pub fn twig_document_serialize(
492        doc: *mut TwigDocument,
493        format: c_int,
494        out_ptr: *mut *const u8,
495        out_len: *mut usize,
496    ) -> TwigStatus;
497    pub fn twig_document_ast_json(
498        doc: *mut TwigDocument,
499        out_ptr: *mut *const u8,
500        out_len: *mut usize,
501    ) -> TwigStatus;
502    pub fn twig_document_query(
503        doc: *mut TwigDocument,
504        selector: *const u8,
505        selector_len: usize,
506        out_ptr: *mut *const TwigQueryMatch,
507        out_len: *mut usize,
508    ) -> TwigStatus;
509    pub fn twig_document_node_span(
510        doc: *mut TwigDocument,
511        node_id: u32,
512        out_span: *mut TwigSpan,
513    ) -> TwigStatus;
514    pub fn twig_document_node_content_span(
515        doc: *mut TwigDocument,
516        node_id: u32,
517        out_span: *mut TwigSpan,
518    ) -> TwigStatus;
519    pub fn twig_document_node_marker_span(
520        doc: *mut TwigDocument,
521        node_id: u32,
522        out_span: *mut TwigSpan,
523    ) -> TwigStatus;
524    pub fn twig_document_attrs_span(
525        doc: *mut TwigDocument,
526        node_id: u32,
527        out_span: *mut TwigSpan,
528    ) -> TwigStatus;
529    pub fn twig_document_line_prefix(
530        doc: *mut TwigDocument,
531        offset: usize,
532        out_span: *mut TwigSpan,
533    ) -> TwigStatus;
534    pub fn twig_document_continuation_prefix(
535        doc: *mut TwigDocument,
536        offset: usize,
537        out_ptr: *mut *const u8,
538        out_len: *mut usize,
539        out_columns: *mut usize,
540    ) -> TwigStatus;
541    pub fn twig_document_blank_line_prefix(
542        doc: *mut TwigDocument,
543        offset: usize,
544        out_ptr: *mut *const u8,
545        out_len: *mut usize,
546        out_columns: *mut usize,
547    ) -> TwigStatus;
548    pub fn twig_document_cell_colspan(
549        doc: *mut TwigDocument,
550        node_id: u32,
551        out_colspan: *mut u32,
552    ) -> TwigStatus;
553    pub fn twig_document_cell_rowspan(
554        doc: *mut TwigDocument,
555        node_id: u32,
556        out_rowspan: *mut u32,
557    ) -> TwigStatus;
558    pub fn twig_document_nodes(
559        doc: *mut TwigDocument,
560        out_ptr: *mut *const TwigFlatNode,
561        out_len: *mut usize,
562    ) -> TwigStatus;
563    pub fn twig_document_definitions(
564        doc: *mut TwigDocument,
565        out_ptr: *mut *const TwigQueryMatch,
566        out_len: *mut usize,
567    ) -> TwigStatus;
568    pub fn twig_document_diagnostics(
569        doc: *mut TwigDocument,
570        format: c_int,
571        out_ptr: *mut *const TwigWarning,
572        out_len: *mut usize,
573    ) -> TwigStatus;
574    pub fn twig_document_children(
575        doc: *mut TwigDocument,
576        node_id: u32,
577        out_ptr: *mut *const TwigQueryMatch,
578        out_len: *mut usize,
579    ) -> TwigStatus;
580    pub fn twig_document_subtree(
581        doc: *mut TwigDocument,
582        node_id: u32,
583        out_ptr: *mut *const TwigFlatNode,
584        out_len: *mut usize,
585    ) -> TwigStatus;
586    pub fn twig_document_node_at(
587        doc: *mut TwigDocument,
588        offset: usize,
589        out_match: *mut TwigQueryMatch,
590    ) -> TwigStatus;
591    pub fn twig_document_nodes_at(
592        doc: *mut TwigDocument,
593        offset: usize,
594        out_ptr: *mut *const TwigQueryMatch,
595        out_len: *mut usize,
596    ) -> TwigStatus;
597    pub fn twig_document_node_at_caret(
598        doc: *mut TwigDocument,
599        offset: usize,
600        out_match: *mut TwigQueryMatch,
601    ) -> TwigStatus;
602    pub fn twig_document_nodes_at_caret(
603        doc: *mut TwigDocument,
604        offset: usize,
605        out_ptr: *mut *const TwigQueryMatch,
606        out_len: *mut usize,
607    ) -> TwigStatus;
608
609    pub fn twig_editor_create(
610        input: *const u8,
611        input_len: usize,
612        format: c_int,
613        out_editor: *mut *mut TwigEditor,
614    ) -> TwigStatus;
615    pub fn twig_editor_create_ext(
616        input: *const u8,
617        input_len: usize,
618        format: c_int,
619        md_flags: u32,
620        out_editor: *mut *mut TwigEditor,
621    ) -> TwigStatus;
622    pub fn twig_editor_destroy(editor: *mut TwigEditor);
623    pub fn twig_editor_replace(
624        editor: *mut TwigEditor,
625        locator: *const u8,
626        locator_len: usize,
627        text: *const u8,
628        text_len: usize,
629    ) -> TwigStatus;
630    pub fn twig_editor_replace_content(
631        editor: *mut TwigEditor,
632        locator: *const u8,
633        locator_len: usize,
634        text: *const u8,
635        text_len: usize,
636    ) -> TwigStatus;
637    pub fn twig_editor_insert_before(
638        editor: *mut TwigEditor,
639        locator: *const u8,
640        locator_len: usize,
641        text: *const u8,
642        text_len: usize,
643    ) -> TwigStatus;
644    pub fn twig_editor_insert_after(
645        editor: *mut TwigEditor,
646        locator: *const u8,
647        locator_len: usize,
648        text: *const u8,
649        text_len: usize,
650    ) -> TwigStatus;
651    pub fn twig_editor_insert_child(
652        editor: *mut TwigEditor,
653        locator: *const u8,
654        locator_len: usize,
655        child_index: usize,
656        text: *const u8,
657        text_len: usize,
658    ) -> TwigStatus;
659    pub fn twig_editor_delete(
660        editor: *mut TwigEditor,
661        locator: *const u8,
662        locator_len: usize,
663    ) -> TwigStatus;
664    pub fn twig_editor_delete_smart(
665        editor: *mut TwigEditor,
666        locator: *const u8,
667        locator_len: usize,
668    ) -> TwigStatus;
669    pub fn twig_editor_unwrap(
670        editor: *mut TwigEditor,
671        locator: *const u8,
672        locator_len: usize,
673    ) -> TwigStatus;
674    pub fn twig_editor_move_before(
675        editor: *mut TwigEditor,
676        locator: *const u8,
677        locator_len: usize,
678        anchor: *const u8,
679        anchor_len: usize,
680    ) -> TwigStatus;
681    pub fn twig_editor_move_after(
682        editor: *mut TwigEditor,
683        locator: *const u8,
684        locator_len: usize,
685        anchor: *const u8,
686        anchor_len: usize,
687    ) -> TwigStatus;
688    pub fn twig_editor_filter(
689        editor: *mut TwigEditor,
690        drop: *const u8,
691        drop_len: usize,
692        keep: *const u8,
693        keep_len: usize,
694        unwrap_kept: c_int,
695    ) -> TwigStatus;
696    pub fn twig_editor_source(
697        editor: *mut TwigEditor,
698        out_ptr: *mut *const u8,
699        out_len: *mut usize,
700    ) -> TwigStatus;
701    pub fn twig_editor_document(
702        editor: *mut TwigEditor,
703        out_doc: *mut *mut TwigDocument,
704    ) -> TwigStatus;
705    pub fn twig_editor_ast_json(
706        editor: *mut TwigEditor,
707        out_ptr: *mut *const u8,
708        out_len: *mut usize,
709    ) -> TwigStatus;
710    pub fn twig_editor_query(
711        editor: *mut TwigEditor,
712        selector: *const u8,
713        selector_len: usize,
714        out_ptr: *mut *const TwigQueryMatch,
715        out_len: *mut usize,
716    ) -> TwigStatus;
717    pub fn twig_editor_edit_range(
718        editor: *mut TwigEditor,
719        start: usize,
720        end: usize,
721        text: *const u8,
722        text_len: usize,
723        out_change: *mut TwigChange,
724    ) -> TwigStatus;
725    pub fn twig_editor_last_change(
726        editor: *mut TwigEditor,
727        out_change: *mut TwigChange,
728    ) -> TwigStatus;
729    pub fn twig_editor_undo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
730    pub fn twig_editor_redo(editor: *mut TwigEditor, out_change: *mut TwigChange) -> TwigStatus;
731    pub fn twig_editor_coalesce_last(editor: *mut TwigEditor) -> TwigStatus;
732    pub fn twig_editor_revision(editor: *mut TwigEditor) -> u64;
733    pub fn twig_editor_dirty_range(editor: *mut TwigEditor, out_span: *mut TwigSpan) -> TwigStatus;
734    pub fn twig_editor_clear_dirty(editor: *mut TwigEditor) -> TwigStatus;
735    pub fn twig_editor_set_caret_blob(
736        editor: *mut TwigEditor,
737        blob_ptr: *const u8,
738        blob_len: usize,
739    ) -> TwigStatus;
740    pub fn twig_editor_caret_blob(
741        editor: *mut TwigEditor,
742        out_ptr: *mut *const u8,
743        out_len: *mut usize,
744    ) -> TwigStatus;
745    pub fn twig_editor_nodes(
746        editor: *mut TwigEditor,
747        out_ptr: *mut *const TwigFlatNode,
748        out_len: *mut usize,
749    ) -> TwigStatus;
750    pub fn twig_editor_child_spans(
751        editor: *mut TwigEditor,
752        node_id: u32,
753        out_ptr: *mut *const TwigQueryMatch,
754        out_len: *mut usize,
755    ) -> TwigStatus;
756    pub fn twig_editor_subtree(
757        editor: *mut TwigEditor,
758        node_id: u32,
759        out_ptr: *mut *const TwigFlatNode,
760        out_len: *mut usize,
761    ) -> TwigStatus;
762    pub fn twig_editor_node_at(
763        editor: *mut TwigEditor,
764        offset: usize,
765        out_match: *mut TwigQueryMatch,
766    ) -> TwigStatus;
767    pub fn twig_editor_nodes_at(
768        editor: *mut TwigEditor,
769        offset: usize,
770        out_ptr: *mut *const TwigQueryMatch,
771        out_len: *mut usize,
772    ) -> TwigStatus;
773    pub fn twig_editor_wrap_range(
774        editor: *mut TwigEditor,
775        start: usize,
776        end: usize,
777        kind: c_int,
778        out_change: *mut TwigChange,
779    ) -> TwigStatus;
780    pub fn twig_editor_toggle_inline(
781        editor: *mut TwigEditor,
782        start: usize,
783        end: usize,
784        kind: c_int,
785        out_change: *mut TwigChange,
786    ) -> TwigStatus;
787    pub fn twig_editor_set_block(
788        editor: *mut TwigEditor,
789        offset: usize,
790        block_kind: c_int,
791        level: u32,
792        out_change: *mut TwigChange,
793    ) -> TwigStatus;
794    pub fn twig_editor_toggle_block_container(
795        editor: *mut TwigEditor,
796        start: usize,
797        end: usize,
798        container_kind: c_int,
799        out_change: *mut TwigChange,
800    ) -> TwigStatus;
801    pub fn twig_editor_renumber_ordered_lists(
802        editor: *mut TwigEditor,
803        offset: usize,
804        out_change: *mut TwigChange,
805    ) -> TwigStatus;
806    /// Whether `format` can spell `gesture` — the toolbar's gray-out question,
807    /// asked without a document. `kind` is read in that gesture's own kind
808    /// space (a `TWIG_GESTURE_*` doc comment in `twig.h` lists which), and must
809    /// be 0 for a gesture that takes none.
810    pub fn twig_format_supports(
811        format: c_int,
812        gesture: c_int,
813        kind: c_int,
814        out_supported: *mut c_int,
815    ) -> TwigStatus;
816    /// `twig_format_supports` for a document parsed with `md_flags` — the
817    /// question asked of the table the editor actually holds. A Markdown
818    /// extension can widen what may be authored: `==x==` is text under default
819    /// options and a `mark` under `TWIG_MD_HIGHLIGHT`, and
820    /// `TWIG_GESTURE_SET_MARK_COLOR` needs `TWIG_MD_HIGHLIGHT_COLORS` on top.
821    pub fn twig_format_supports_ext(
822        format: c_int,
823        md_flags: u32,
824        gesture: c_int,
825        kind: c_int,
826        out_supported: *mut c_int,
827    ) -> TwigStatus;
828    /// Whether `format` can be authored into at all — 0 for a parse-only
829    /// format. A weaker claim than it looks; see `twig_format_supports`.
830    pub fn twig_format_is_authorable(format: c_int, out_authorable: *mut c_int) -> TwigStatus;
831    pub fn twig_editor_table_edit(
832        editor: *mut TwigEditor,
833        offset: usize,
834        op: c_int,
835        arg: c_int,
836        out_change: *mut TwigChange,
837    ) -> TwigStatus;
838    pub fn twig_editor_insert_table(
839        editor: *mut TwigEditor,
840        offset: usize,
841        rows: usize,
842        cols: usize,
843        out_change: *mut TwigChange,
844    ) -> TwigStatus;
845    pub fn twig_editor_insert_directive(
846        editor: *mut TwigEditor,
847        offset: usize,
848        name_ptr: *const u8,
849        name_len: usize,
850        label_ptr: *const u8,
851        label_len: usize,
852        attrs_ptr: *const TwigKeyVal,
853        attrs_len: usize,
854        out_change: *mut TwigChange,
855    ) -> TwigStatus;
856    pub fn twig_editor_set_block_attrs(
857        editor: *mut TwigEditor,
858        offset: usize,
859        attrs_ptr: *const TwigKeyVal,
860        attrs_len: usize,
861        out_change: *mut TwigChange,
862    ) -> TwigStatus;
863    pub fn twig_editor_wrap_range_attrs(
864        editor: *mut TwigEditor,
865        start: usize,
866        end: usize,
867        attrs_ptr: *const TwigKeyVal,
868        attrs_len: usize,
869        out_change: *mut TwigChange,
870    ) -> TwigStatus;
871    pub fn twig_editor_set_node_attrs(
872        editor: *mut TwigEditor,
873        node_id: u32,
874        attrs_ptr: *const TwigKeyVal,
875        attrs_len: usize,
876        out_change: *mut TwigChange,
877    ) -> TwigStatus;
878    pub fn twig_editor_insert_link(
879        editor: *mut TwigEditor,
880        start: usize,
881        end: usize,
882        destination: *const u8,
883        destination_len: usize,
884        out_change: *mut TwigChange,
885    ) -> TwigStatus;
886    pub fn twig_editor_insert_image(
887        editor: *mut TwigEditor,
888        start: usize,
889        end: usize,
890        destination: *const u8,
891        destination_len: usize,
892        out_change: *mut TwigChange,
893    ) -> TwigStatus;
894
895    pub fn twig_editor_insert_literal(
896        editor: *mut TwigEditor,
897        offset: usize,
898        text: *const u8,
899        text_len: usize,
900        out_change: *mut TwigChange,
901    ) -> TwigStatus;
902
903    pub fn twig_editor_insert_line_break(
904        editor: *mut TwigEditor,
905        offset: usize,
906        out_change: *mut TwigChange,
907    ) -> TwigStatus;
908
909    pub fn twig_editor_insert_thematic_break(
910        editor: *mut TwigEditor,
911        offset: usize,
912        out_change: *mut TwigChange,
913    ) -> TwigStatus;
914
915    pub fn twig_editor_split_block(
916        editor: *mut TwigEditor,
917        offset: usize,
918        out_change: *mut TwigChange,
919    ) -> TwigStatus;
920
921    pub fn twig_editor_join_blocks(
922        editor: *mut TwigEditor,
923        offset: usize,
924        out_change: *mut TwigChange,
925    ) -> TwigStatus;
926
927    pub fn twig_editor_move_block(
928        editor: *mut TwigEditor,
929        from: usize,
930        to: usize,
931        out_change: *mut TwigChange,
932    ) -> TwigStatus;
933    // `language` is OPTIONAL, carried as this ABI's (ptr, len, has_*) triple:
934    // has_language == 0 leaves the fence bare, and "absent" is a different
935    // request from "present but empty".
936    pub fn twig_editor_toggle_code_block(
937        editor: *mut TwigEditor,
938        start: usize,
939        end: usize,
940        language: *const u8,
941        language_len: usize,
942        has_language: c_int,
943        out_change: *mut TwigChange,
944    ) -> TwigStatus;
945    pub fn twig_editor_set_code_language(
946        editor: *mut TwigEditor,
947        offset: usize,
948        language: *const u8,
949        language_len: usize,
950        has_language: c_int,
951        out_change: *mut TwigChange,
952    ) -> TwigStatus;
953    /// Set — or clear, with `has_color == 0` — the colour of the highlight the
954    /// caret at `offset` is inside. Markdown with `TWIG_MD_HIGHLIGHT_COLORS`
955    /// only; the name is a `data-color` value (`red`, `blue`, …).
956    pub fn twig_editor_set_mark_color(
957        editor: *mut TwigEditor,
958        offset: usize,
959        color: *const u8,
960        color_len: usize,
961        has_color: c_int,
962        out_change: *mut TwigChange,
963    ) -> TwigStatus;
964    pub fn twig_editor_toggle_task_item(
965        editor: *mut TwigEditor,
966        offset: usize,
967        out_change: *mut TwigChange,
968    ) -> TwigStatus;
969    pub fn twig_editor_set_task_checked(
970        editor: *mut TwigEditor,
971        offset: usize,
972        checked: c_int,
973        out_change: *mut TwigChange,
974    ) -> TwigStatus;
975    pub fn twig_editor_toggle_task_checked(
976        editor: *mut TwigEditor,
977        offset: usize,
978        out_change: *mut TwigChange,
979    ) -> TwigStatus;
980    pub fn twig_editor_insert_footnote(
981        editor: *mut TwigEditor,
982        offset: usize,
983        label: *const u8,
984        label_len: usize,
985        out_change: *mut TwigChange,
986    ) -> TwigStatus;
987    pub fn twig_editor_insert_inline_math(
988        editor: *mut TwigEditor,
989        offset: usize,
990        formula: *const u8,
991        formula_len: usize,
992        out_change: *mut TwigChange,
993    ) -> TwigStatus;
994    pub fn twig_editor_insert_display_math(
995        editor: *mut TwigEditor,
996        offset: usize,
997        formula: *const u8,
998        formula_len: usize,
999        out_change: *mut TwigChange,
1000    ) -> TwigStatus;
1001
1002    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
1003    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
1004    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32)
1005    -> TwigStatus;
1006    pub fn twig_builder_add_text(
1007        builder: *mut TwigBuilder,
1008        kind: c_int,
1009        text: *const u8,
1010        text_len: usize,
1011        out_id: *mut u32,
1012    ) -> TwigStatus;
1013    pub fn twig_builder_add_heading(
1014        builder: *mut TwigBuilder,
1015        level: u32,
1016        out_id: *mut u32,
1017    ) -> TwigStatus;
1018    pub fn twig_builder_add_code_block(
1019        builder: *mut TwigBuilder,
1020        lang: *const u8,
1021        lang_len: usize,
1022        has_lang: c_int,
1023        text: *const u8,
1024        text_len: usize,
1025        out_id: *mut u32,
1026    ) -> TwigStatus;
1027    pub fn twig_builder_add_raw_block(
1028        builder: *mut TwigBuilder,
1029        format: *const u8,
1030        format_len: usize,
1031        text: *const u8,
1032        text_len: usize,
1033        out_id: *mut u32,
1034    ) -> TwigStatus;
1035    pub fn twig_builder_add_metadata(
1036        builder: *mut TwigBuilder,
1037        lang: *const u8,
1038        lang_len: usize,
1039        text: *const u8,
1040        text_len: usize,
1041        out_id: *mut u32,
1042    ) -> TwigStatus;
1043    pub fn twig_builder_add_raw_inline(
1044        builder: *mut TwigBuilder,
1045        format: *const u8,
1046        format_len: usize,
1047        text: *const u8,
1048        text_len: usize,
1049        out_id: *mut u32,
1050    ) -> TwigStatus;
1051    pub fn twig_builder_add_smart_punctuation(
1052        builder: *mut TwigBuilder,
1053        punct_kind: c_int,
1054        text: *const u8,
1055        text_len: usize,
1056        out_id: *mut u32,
1057    ) -> TwigStatus;
1058    pub fn twig_builder_add_link(
1059        builder: *mut TwigBuilder,
1060        destination: *const u8,
1061        destination_len: usize,
1062        has_destination: c_int,
1063        reference: *const u8,
1064        reference_len: usize,
1065        has_reference: c_int,
1066        out_id: *mut u32,
1067    ) -> TwigStatus;
1068    pub fn twig_builder_add_image(
1069        builder: *mut TwigBuilder,
1070        destination: *const u8,
1071        destination_len: usize,
1072        has_destination: c_int,
1073        reference: *const u8,
1074        reference_len: usize,
1075        has_reference: c_int,
1076        out_id: *mut u32,
1077    ) -> TwigStatus;
1078    pub fn twig_builder_add_directive(
1079        builder: *mut TwigBuilder,
1080        form: c_int,
1081        name: *const u8,
1082        name_len: usize,
1083        out_id: *mut u32,
1084    ) -> TwigStatus;
1085    pub fn twig_builder_add_element(
1086        builder: *mut TwigBuilder,
1087        name: *const u8,
1088        name_len: usize,
1089        out_id: *mut u32,
1090    ) -> TwigStatus;
1091    pub fn twig_builder_add_processing_instruction(
1092        builder: *mut TwigBuilder,
1093        target: *const u8,
1094        target_len: usize,
1095        data: *const u8,
1096        data_len: usize,
1097        out_id: *mut u32,
1098    ) -> TwigStatus;
1099    pub fn twig_builder_add_footnote(
1100        builder: *mut TwigBuilder,
1101        label: *const u8,
1102        label_len: usize,
1103        out_id: *mut u32,
1104    ) -> TwigStatus;
1105    pub fn twig_builder_add_citation(
1106        builder: *mut TwigBuilder,
1107        label: *const u8,
1108        label_len: usize,
1109        out_id: *mut u32,
1110    ) -> TwigStatus;
1111    pub fn twig_builder_add_substitution(
1112        builder: *mut TwigBuilder,
1113        label: *const u8,
1114        label_len: usize,
1115        out_id: *mut u32,
1116    ) -> TwigStatus;
1117    pub fn twig_builder_add_reference(
1118        builder: *mut TwigBuilder,
1119        label: *const u8,
1120        label_len: usize,
1121        destination: *const u8,
1122        destination_len: usize,
1123        out_id: *mut u32,
1124    ) -> TwigStatus;
1125    pub fn twig_builder_add_bullet_list(
1126        builder: *mut TwigBuilder,
1127        style: c_int,
1128        tight: c_int,
1129        out_id: *mut u32,
1130    ) -> TwigStatus;
1131    pub fn twig_builder_add_ordered_list(
1132        builder: *mut TwigBuilder,
1133        numbering: c_int,
1134        delim: c_int,
1135        tight: c_int,
1136        start: u32,
1137        has_start: c_int,
1138        out_id: *mut u32,
1139    ) -> TwigStatus;
1140    pub fn twig_builder_add_task_list(
1141        builder: *mut TwigBuilder,
1142        tight: c_int,
1143        out_id: *mut u32,
1144    ) -> TwigStatus;
1145    pub fn twig_builder_add_task_list_item(
1146        builder: *mut TwigBuilder,
1147        checked: c_int,
1148        out_id: *mut u32,
1149    ) -> TwigStatus;
1150    pub fn twig_builder_add_row(
1151        builder: *mut TwigBuilder,
1152        head: c_int,
1153        out_id: *mut u32,
1154    ) -> TwigStatus;
1155    pub fn twig_builder_add_cell(
1156        builder: *mut TwigBuilder,
1157        head: c_int,
1158        alignment: c_int,
1159        out_id: *mut u32,
1160    ) -> TwigStatus;
1161    pub fn twig_builder_add_cell_spanning(
1162        builder: *mut TwigBuilder,
1163        head: c_int,
1164        alignment: c_int,
1165        colspan: u32,
1166        rowspan: u32,
1167        out_id: *mut u32,
1168    ) -> TwigStatus;
1169    pub fn twig_builder_set_children(
1170        builder: *mut TwigBuilder,
1171        parent: u32,
1172        ids: *const u32,
1173        ids_len: usize,
1174    ) -> TwigStatus;
1175    pub fn twig_builder_set_attrs(
1176        builder: *mut TwigBuilder,
1177        id: u32,
1178        kvs: *const TwigKeyVal,
1179        kvs_len: usize,
1180    ) -> TwigStatus;
1181    pub fn twig_builder_render_html(
1182        builder: *mut TwigBuilder,
1183        root: u32,
1184        out_ptr: *mut *const u8,
1185        out_len: *mut usize,
1186    ) -> TwigStatus;
1187    pub fn twig_builder_serialize(
1188        builder: *mut TwigBuilder,
1189        root: u32,
1190        format: c_int,
1191        out_ptr: *mut *const u8,
1192        out_len: *mut usize,
1193    ) -> TwigStatus;
1194    pub fn twig_builder_ast_json(
1195        builder: *mut TwigBuilder,
1196        root: u32,
1197        out_ptr: *mut *const u8,
1198        out_len: *mut usize,
1199    ) -> TwigStatus;
1200    pub fn twig_builder_query(
1201        builder: *mut TwigBuilder,
1202        root: u32,
1203        selector: *const u8,
1204        selector_len: usize,
1205        out_ptr: *mut *const TwigQueryMatch,
1206        out_len: *mut usize,
1207    ) -> TwigStatus;
1208}