Skip to main content

twig/
ffi.rs

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