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