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    pub fn twig_editor_toggle_block_container(
360        editor: *mut TwigEditor,
361        start: usize,
362        end: usize,
363        container_kind: c_int,
364        out_change: *mut TwigChange,
365    ) -> TwigStatus;
366    pub fn twig_editor_insert_link(
367        editor: *mut TwigEditor,
368        start: usize,
369        end: usize,
370        destination: *const u8,
371        destination_len: usize,
372        out_change: *mut TwigChange,
373    ) -> TwigStatus;
374
375    pub fn twig_builder_create(out_builder: *mut *mut TwigBuilder) -> TwigStatus;
376    pub fn twig_builder_destroy(builder: *mut TwigBuilder);
377    pub fn twig_builder_add(builder: *mut TwigBuilder, kind: c_int, out_id: *mut u32) -> TwigStatus;
378    pub fn twig_builder_add_text(
379        builder: *mut TwigBuilder,
380        kind: c_int,
381        text: *const u8,
382        text_len: usize,
383        out_id: *mut u32,
384    ) -> TwigStatus;
385    pub fn twig_builder_add_heading(builder: *mut TwigBuilder, level: u32, out_id: *mut u32) -> TwigStatus;
386    pub fn twig_builder_add_code_block(
387        builder: *mut TwigBuilder,
388        lang: *const u8,
389        lang_len: usize,
390        has_lang: c_int,
391        text: *const u8,
392        text_len: usize,
393        out_id: *mut u32,
394    ) -> TwigStatus;
395    pub fn twig_builder_add_raw_block(
396        builder: *mut TwigBuilder,
397        format: *const u8,
398        format_len: usize,
399        text: *const u8,
400        text_len: usize,
401        out_id: *mut u32,
402    ) -> TwigStatus;
403    pub fn twig_builder_add_metadata(
404        builder: *mut TwigBuilder,
405        lang: *const u8,
406        lang_len: usize,
407        text: *const u8,
408        text_len: usize,
409        out_id: *mut u32,
410    ) -> TwigStatus;
411    pub fn twig_builder_add_raw_inline(
412        builder: *mut TwigBuilder,
413        format: *const u8,
414        format_len: usize,
415        text: *const u8,
416        text_len: usize,
417        out_id: *mut u32,
418    ) -> TwigStatus;
419    pub fn twig_builder_add_smart_punctuation(
420        builder: *mut TwigBuilder,
421        punct_kind: c_int,
422        text: *const u8,
423        text_len: usize,
424        out_id: *mut u32,
425    ) -> TwigStatus;
426    pub fn twig_builder_add_link(
427        builder: *mut TwigBuilder,
428        destination: *const u8,
429        destination_len: usize,
430        has_destination: c_int,
431        reference: *const u8,
432        reference_len: usize,
433        has_reference: c_int,
434        out_id: *mut u32,
435    ) -> TwigStatus;
436    pub fn twig_builder_add_image(
437        builder: *mut TwigBuilder,
438        destination: *const u8,
439        destination_len: usize,
440        has_destination: c_int,
441        reference: *const u8,
442        reference_len: usize,
443        has_reference: c_int,
444        out_id: *mut u32,
445    ) -> TwigStatus;
446    pub fn twig_builder_add_directive(
447        builder: *mut TwigBuilder,
448        form: c_int,
449        name: *const u8,
450        name_len: usize,
451        out_id: *mut u32,
452    ) -> TwigStatus;
453    pub fn twig_builder_add_element(
454        builder: *mut TwigBuilder,
455        name: *const u8,
456        name_len: usize,
457        out_id: *mut u32,
458    ) -> TwigStatus;
459    pub fn twig_builder_add_processing_instruction(
460        builder: *mut TwigBuilder,
461        target: *const u8,
462        target_len: usize,
463        data: *const u8,
464        data_len: usize,
465        out_id: *mut u32,
466    ) -> TwigStatus;
467    pub fn twig_builder_add_footnote(
468        builder: *mut TwigBuilder,
469        label: *const u8,
470        label_len: usize,
471        out_id: *mut u32,
472    ) -> TwigStatus;
473    pub fn twig_builder_add_reference(
474        builder: *mut TwigBuilder,
475        label: *const u8,
476        label_len: usize,
477        destination: *const u8,
478        destination_len: usize,
479        out_id: *mut u32,
480    ) -> TwigStatus;
481    pub fn twig_builder_add_bullet_list(
482        builder: *mut TwigBuilder,
483        style: c_int,
484        tight: c_int,
485        out_id: *mut u32,
486    ) -> TwigStatus;
487    pub fn twig_builder_add_ordered_list(
488        builder: *mut TwigBuilder,
489        numbering: c_int,
490        delim: c_int,
491        tight: c_int,
492        start: u32,
493        has_start: c_int,
494        out_id: *mut u32,
495    ) -> TwigStatus;
496    pub fn twig_builder_add_task_list(builder: *mut TwigBuilder, tight: c_int, out_id: *mut u32) -> TwigStatus;
497    pub fn twig_builder_add_task_list_item(builder: *mut TwigBuilder, checked: c_int, out_id: *mut u32) -> TwigStatus;
498    pub fn twig_builder_add_row(builder: *mut TwigBuilder, head: c_int, out_id: *mut u32) -> TwigStatus;
499    pub fn twig_builder_add_cell(builder: *mut TwigBuilder, head: c_int, alignment: c_int, out_id: *mut u32) -> TwigStatus;
500    pub fn twig_builder_set_children(
501        builder: *mut TwigBuilder,
502        parent: u32,
503        ids: *const u32,
504        ids_len: usize,
505    ) -> TwigStatus;
506    pub fn twig_builder_set_attrs(
507        builder: *mut TwigBuilder,
508        id: u32,
509        kvs: *const TwigKeyVal,
510        kvs_len: usize,
511    ) -> TwigStatus;
512    pub fn twig_builder_render_html(
513        builder: *mut TwigBuilder,
514        root: u32,
515        out_ptr: *mut *const u8,
516        out_len: *mut usize,
517    ) -> TwigStatus;
518    pub fn twig_builder_serialize(
519        builder: *mut TwigBuilder,
520        root: u32,
521        format: c_int,
522        out_ptr: *mut *const u8,
523        out_len: *mut usize,
524    ) -> TwigStatus;
525    pub fn twig_builder_ast_json(
526        builder: *mut TwigBuilder,
527        root: u32,
528        out_ptr: *mut *const u8,
529        out_len: *mut usize,
530    ) -> TwigStatus;
531    pub fn twig_builder_query(
532        builder: *mut TwigBuilder,
533        root: u32,
534        selector: *const u8,
535        selector_len: usize,
536        out_ptr: *mut *const TwigQueryMatch,
537        out_len: *mut usize,
538    ) -> TwigStatus;
539}