Skip to main content

fig_sys/
lib.rs

1//! Low-level FFI bindings for fig's C ABI, plus the build script that compiles
2//! and links the native `libfig.a`.
3//!
4//! This is the `-sys` layer: raw `extern "C"` functions, `#[repr(C)]` type
5//! mirrors, and status/format constants — nothing else. The safe, ergonomic
6//! API (and serde/derive integration) lives in the `fig` crate, which depends
7//! on this one. Direct use is `unsafe` and unstable across ABI-version bumps.
8#![allow(non_camel_case_types)]
9
10use std::os::raw::{c_char, c_int, c_void};
11
12/// A fig C ABI status code, as a transparent wrapper over the raw `c_int` the
13/// ABI returns — deliberately *not* a Rust `enum`.
14///
15/// A fieldless `#[repr(C)]` enum returned by value from an `extern "C"` function
16/// is undefined behavior the instant the callee returns a discriminant the enum
17/// does not list, and fig's status set is allowed to grow after 1.0. The newtype
18/// preserves any code unchanged; compare against the associated constants and
19/// route unrecognized values through a fallback (the `fig` crate does this in
20/// its `Error::from_status`).
21#[repr(transparent)]
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub struct FigStatus(pub c_int);
24
25#[allow(dead_code)]
26impl FigStatus {
27    pub const OK: c_int = 0;
28    pub const INVALID_ARGUMENT: c_int = 1;
29    pub const PARSE_ERROR: c_int = 2;
30    pub const OUT_OF_MEMORY: c_int = 3;
31    pub const UNSUPPORTED_FORMAT: c_int = 4;
32    pub const NOT_FOUND: c_int = 5;
33    /// The operation is not defined for these arguments, though each is
34    /// individually valid. Added in core 2.7.0; see `fig_embed_retype`.
35    pub const UNSUPPORTED_OPERATION: c_int = 6;
36    pub const INTERNAL_ERROR: c_int = 255;
37}
38
39#[repr(C)]
40#[derive(Clone, Copy, Debug, Eq, PartialEq)]
41pub enum FigFormat {
42    Json = 1,
43    Jsonc = 2,
44    Yaml = 3,
45    Toml = 4,
46    Zon = 5,
47    // 6 was generic XML through core 2.x; retired with the format in core
48    // 3.0 and never reused, which is why JSON5 keeps its gap.
49    Json5 = 7,
50    // The native `fig` authoring dialect. Appended, same reasoning as JSON5.
51    Fig = 8,
52    Ini = 9,
53    Dotenv = 10,
54    Properties = 11,
55    Plist = 12,
56    Nestedtext = 13,
57}
58
59/// Mirror of fig.h's `FIG_FORMAT_RUNTIME_BASE`: a format integer at or above
60/// this names a language registered at runtime, assigned per process and
61/// never a `FigFormat` variant. Every compiled-in variant above is below it.
62pub const FIG_FORMAT_RUNTIME_BASE: c_int = 4096;
63
64pub enum FigDocument {}
65
66pub type FigNodeId = u32;
67
68/// Sentinel for "no such node", matching `FIG_NODE_NONE` in `fig.h`.
69pub const FIG_NODE_NONE: FigNodeId = 0xFFFF_FFFF;
70
71#[repr(C)]
72#[derive(Clone, Copy, Debug, Eq, PartialEq)]
73#[allow(dead_code)]
74pub enum FigNodeKind {
75    Invalid = -1,
76    Null = 0,
77    Bool = 1,
78    Int = 2,
79    Float = 3,
80    String = 4,
81    Sequence = 5,
82    Mapping = 6,
83    Keyvalue = 7,
84    Alias = 8,
85}
86
87impl FigNodeKind {
88    /// Map the raw `c_int` returned by `fig_node_kind` onto a `FigNodeKind`.
89    /// Unknown / future kinds collapse to [`FigNodeKind::Invalid`] rather than
90    /// being reinterpreted as an out-of-range enum value — which, for a value
91    /// returned by an `extern "C"` function into a Rust enum, is undefined
92    /// behavior. This is the only place a raw kind crosses into the enum.
93    pub fn from_c(raw: c_int) -> Self {
94        match raw {
95            0 => FigNodeKind::Null,
96            1 => FigNodeKind::Bool,
97            2 => FigNodeKind::Int,
98            3 => FigNodeKind::Float,
99            4 => FigNodeKind::String,
100            5 => FigNodeKind::Sequence,
101            6 => FigNodeKind::Mapping,
102            7 => FigNodeKind::Keyvalue,
103            8 => FigNodeKind::Alias,
104            _ => FigNodeKind::Invalid,
105        }
106    }
107}
108
109/// A caller-allocated parse diagnostic. Mirrors `FigError` in `fig.h`. Lead with
110/// `size = size_of::<FigError>()`: the library writes only the fields `size`
111/// covers, so it can gain fields in a later release without breaking this layout.
112/// `byte_offset`/`line`/`column` are 0 when unknown (always 0 in this release —
113/// offset plumbing is a planned core follow-up). `message` is NUL-terminated and
114/// truncated to fit; `message_len` excludes the NUL.
115#[repr(C)]
116#[derive(Clone, Copy)]
117pub struct FigError {
118    pub size: u32,
119    pub code: c_int,
120    pub byte_offset: usize,
121    pub line: u32,
122    pub column: u32,
123    pub message_len: usize,
124    pub message: [u8; 256],
125}
126
127impl FigError {
128    /// A zeroed struct with `size` set, ready to pass to `fig_parse_ex`.
129    pub fn new() -> Self {
130        FigError {
131            size: std::mem::size_of::<FigError>() as u32,
132            code: 0,
133            byte_offset: 0,
134            line: 0,
135            column: 0,
136            message_len: 0,
137            message: [0; 256],
138        }
139    }
140}
141
142unsafe extern "C" {
143    pub fn fig_version() -> u32;
144    pub fn fig_version_string() -> *const std::os::raw::c_char;
145    pub fn fig_format_capabilities(format: c_int) -> u32;
146
147    // Declared for ABI-mirror completeness; the binding parses via `fig_parse_ex`
148    // (richer errors), so this plain entry point is not called from Rust.
149    #[allow(dead_code)]
150    pub fn fig_parse(
151        input: *const u8,
152        input_len: usize,
153        format: c_int,
154        out_doc: *mut *mut FigDocument,
155    ) -> FigStatus;
156
157    pub fn fig_parse_ex(
158        input: *const u8,
159        input_len: usize,
160        format: c_int,
161        out_doc: *mut *mut FigDocument,
162        out_err: *mut FigError,
163    ) -> FigStatus;
164
165    pub fn fig_document_destroy(doc: *mut FigDocument);
166
167    pub fn fig_document_serialize(
168        doc: *mut FigDocument,
169        format: c_int,
170        options: *const FigSerializeOptions,
171        out_ptr: *mut *const u8,
172        out_len: *mut usize,
173    ) -> FigStatus;
174}
175
176// Read traversal — consumed by `Document::to_value` and the serde deserializer.
177unsafe extern "C" {
178    pub fn fig_document_root(doc: *const FigDocument) -> FigNodeId;
179    // Returns the raw kind as `c_int`, not `FigNodeKind`: decoding it directly
180    // into the enum would be UB if the core returned an unlisted value. Callers
181    // go through `FigNodeKind::from_c`.
182    pub fn fig_node_kind(doc: *const FigDocument, node: FigNodeId) -> c_int;
183    pub fn fig_node_first_child(doc: *const FigDocument, node: FigNodeId) -> FigNodeId;
184    pub fn fig_node_next_sibling(doc: *const FigDocument, node: FigNodeId) -> FigNodeId;
185    pub fn fig_node_child_count(doc: *const FigDocument, node: FigNodeId) -> usize;
186    pub fn fig_keyvalue_key(doc: *const FigDocument, node: FigNodeId) -> FigNodeId;
187    pub fn fig_keyvalue_value(doc: *const FigDocument, node: FigNodeId) -> FigNodeId;
188
189    pub fn fig_node_bool(doc: *const FigDocument, node: FigNodeId, out: *mut bool) -> bool;
190    pub fn fig_node_number(
191        doc: *const FigDocument,
192        node: FigNodeId,
193        out_ptr: *mut *const u8,
194        out_len: *mut usize,
195    ) -> bool;
196    pub fn fig_node_string(
197        doc: *const FigDocument,
198        node: FigNodeId,
199        out_ptr: *mut *const u8,
200        out_len: *mut usize,
201    ) -> bool;
202    pub fn fig_node_extended(
203        doc: *const FigDocument,
204        node: FigNodeId,
205        out_kind: *mut c_int,
206        out_ptr: *mut *const u8,
207        out_len: *mut usize,
208    ) -> bool;
209}
210
211// ---- value construction + serialization ----
212
213pub enum FigValue {}
214
215/// A `key: value` entry for `fig_value_map`. Mirrors `FigKeyValue` in `fig.h`.
216#[repr(C)]
217#[derive(Clone, Copy, Debug)]
218pub struct FigKeyValue {
219    pub key: FigNodeId,
220    pub value: FigNodeId,
221}
222
223/// Output style for `fig_value_serialize_opts`/`fig_document_serialize`. Mirrors
224/// `FigSerializeOptions` in `fig.h`. ALL trailing fields are declared explicitly
225/// (not left to struct padding): with `size = size_of` the core reads every byte
226/// up to `size`, so an undeclared `strip_comments`/`lossless` would otherwise be
227/// read out of uninitialized padding.
228#[repr(C)]
229#[derive(Clone, Copy, Debug)]
230pub struct FigSerializeOptions {
231    /// Set to `size_of::<FigSerializeOptions>()`. Version tag for the struct:
232    /// the core reads a field only when `size` covers it, so fields can be
233    /// appended without breaking this layout. See `FigSerializeOptions` in `fig.h`.
234    pub size: u32,
235    pub pretty: u8,
236    pub indent: u8,
237    pub strip_comments: u8,
238    pub lossless: u8,
239    pub width: u16,
240    /// fig-format fragments only: nonzero renders a container root as inline
241    /// flow (`[a, b]` / `{ k = v }`). Set by the editors' splice path (see
242    /// `value_text`); not exposed on the public `SerializeOptions` — inline is
243    /// a property of *where* the text goes, not a caller style preference.
244    pub flow: u8,
245    /// Nonzero renders the value as the editor takes it spliced into a
246    /// document (plist's bare element, a NestedText scalar's plain text).
247    /// Set by the editors' splice path; not exposed on the public
248    /// `SerializeOptions`.
249    pub splice: u8,
250}
251
252/// One lossy event from `fig_*_diagnose`, pulled by index via `fig_*_warning`.
253/// Caller-allocated; lead with `size = size_of::<FigWarning>()` (same policy as
254/// `FigSerializeOptions`/`FigError`). `path`/`note` are NOT NUL-terminated and
255/// borrow the producing handle's diagnostics arena (valid until the next
256/// diagnose on it or its destroy) — copy them out before then. Mirrors
257/// `FigWarning` in `fig.h`.
258#[repr(C)]
259#[derive(Clone, Copy)]
260pub struct FigWarning {
261    pub size: u32,
262    pub code: c_int,
263    pub cause: c_int,
264    pub path: *const u8,
265    pub path_len: usize,
266    pub note: *const u8,
267    pub note_len: usize,
268}
269
270impl FigWarning {
271    /// A zeroed struct with `size` set, ready to pass to a `fig_*_warning` call.
272    pub fn new() -> Self {
273        FigWarning {
274            size: std::mem::size_of::<FigWarning>() as u32,
275            code: 0,
276            cause: 0,
277            path: std::ptr::null(),
278            path_len: 0,
279            note: std::ptr::null(),
280            note_len: 0,
281        }
282    }
283}
284
285unsafe extern "C" {
286    pub fn fig_value_create(out_value: *mut *mut FigValue) -> FigStatus;
287    pub fn fig_value_destroy(value: *mut FigValue);
288
289    pub fn fig_value_null(value: *mut FigValue, out_id: *mut FigNodeId) -> FigStatus;
290    pub fn fig_value_bool(value: *mut FigValue, b: bool, out_id: *mut FigNodeId) -> FigStatus;
291    pub fn fig_value_int(value: *mut FigValue, n: i64, out_id: *mut FigNodeId) -> FigStatus;
292    pub fn fig_value_uint(value: *mut FigValue, n: u64, out_id: *mut FigNodeId) -> FigStatus;
293    pub fn fig_value_number(
294        value: *mut FigValue,
295        raw: *const u8,
296        raw_len: usize,
297        is_float: bool,
298        out_id: *mut FigNodeId,
299    ) -> FigStatus;
300    pub fn fig_value_string(
301        value: *mut FigValue,
302        ptr: *const u8,
303        len: usize,
304        out_id: *mut FigNodeId,
305    ) -> FigStatus;
306    pub fn fig_value_extended(
307        value: *mut FigValue,
308        kind: c_int,
309        text: *const u8,
310        text_len: usize,
311        out_id: *mut FigNodeId,
312    ) -> FigStatus;
313    pub fn fig_value_seq(
314        value: *mut FigValue,
315        items: *const FigNodeId,
316        items_len: usize,
317        out_id: *mut FigNodeId,
318    ) -> FigStatus;
319    pub fn fig_value_map(
320        value: *mut FigValue,
321        entries: *const FigKeyValue,
322        entries_len: usize,
323        out_id: *mut FigNodeId,
324    ) -> FigStatus;
325    // ABI-mirror decl; the binding always serializes through the `_opts` form.
326    #[allow(dead_code)]
327    pub fn fig_value_serialize(
328        value: *mut FigValue,
329        root: FigNodeId,
330        format: c_int,
331        out_ptr: *mut *const u8,
332        out_len: *mut usize,
333    ) -> FigStatus;
334    pub fn fig_value_serialize_opts(
335        value: *mut FigValue,
336        root: FigNodeId,
337        format: c_int,
338        options: *const FigSerializeOptions,
339        out_ptr: *mut *const u8,
340        out_len: *mut usize,
341    ) -> FigStatus;
342}
343
344// ---- serialization diagnostics ----
345
346unsafe extern "C" {
347    pub fn fig_document_diagnose(
348        doc: *mut FigDocument,
349        format: c_int,
350        options: *const FigSerializeOptions,
351        out_count: *mut usize,
352    ) -> FigStatus;
353    pub fn fig_document_warning(
354        doc: *mut FigDocument,
355        index: usize,
356        out: *mut FigWarning,
357    ) -> FigStatus;
358    pub fn fig_value_diagnose(
359        value: *mut FigValue,
360        root: FigNodeId,
361        format: c_int,
362        options: *const FigSerializeOptions,
363        out_count: *mut usize,
364    ) -> FigStatus;
365    pub fn fig_value_warning(value: *mut FigValue, index: usize, out: *mut FigWarning)
366    -> FigStatus;
367}
368
369// ---- editing (write path) ----
370
371pub enum FigEditor {}
372pub enum FigEmbed {}
373
374/// One step of a path: `kind == 0` selects mapping key `key_ptr[0..key_len]`;
375/// `kind == 1` selects sequence element `index`. Mirrors `FigPathSegment` in
376/// `fig.h`.
377#[repr(C)]
378#[derive(Clone, Copy, Debug)]
379pub struct FigPathSegment {
380    pub kind: i32,
381    pub key_ptr: *const u8,
382    pub key_len: usize,
383    pub index: usize,
384}
385
386/// A borrowed UTF-8 string slice (`ptr[0..len]`) passed across the C ABI.
387/// Mirrors `FigStr` in `fig.h`; used for the key list of `*_reorder_keys`.
388#[repr(C)]
389#[derive(Clone, Copy, Debug)]
390pub struct FigStr {
391    pub ptr: *const u8,
392    pub len: usize,
393}
394
395// `FigSpan`/`FigRegion`/`fig_embed_extract` mirror the low-level embed C ABI.
396// The Rust-facing consumer is `Embed` (which uses `fig_embed_*`); these are
397// declared for parity with the header and for any future low-level wrapper.
398#[repr(C)]
399#[derive(Clone, Copy, Debug, Default)]
400#[allow(dead_code)]
401pub struct FigSpan {
402    pub start: usize,
403    pub end: usize,
404}
405
406#[repr(C)]
407#[derive(Clone, Copy, Debug, Default)]
408#[allow(dead_code)]
409pub struct FigRegion {
410    /// Size-version tag: set to `size_of::<FigRegion>()` before
411    /// `fig_embed_extract` so the library only writes the fields this layout
412    /// covers. A zero `size` (e.g. from `Default`) makes the library write
413    /// nothing — always set it explicitly.
414    pub size: u32,
415    pub open_fence: FigSpan,
416    pub content: FigSpan,
417    pub close_fence: FigSpan,
418    pub body: FigSpan,
419    /// `[0, open_fence.start)` and `[close_fence.end, input_len)` — the host
420    /// text on each side of the block. With the three region spans they tile
421    /// the input exactly, so a rebuild loses nothing. Added in core 2.7.0; an older
422    /// `size` leaves them unwritten.
423    pub body_before: FigSpan,
424    pub body_after: FigSpan,
425}
426
427/// The container half of an embed selector; every `fig_embed_*` entry point
428/// that selects a region takes one of these beside a `FigFormat`. The four
429/// parametric containers read the format; the three presets pin their own
430/// and ignore it. Mirrors `FigEmbedContainer` in `fig.h` (ABI 2).
431#[repr(C)]
432#[derive(Clone, Copy, Debug, Eq, PartialEq)]
433#[allow(dead_code)]
434pub enum FigEmbedContainer {
435    MdFrontmatter = 0,
436    Fenced = 1,
437    HtmlScript = 2,
438    HtmlCode = 3,
439    SemicolonsJson = 4,
440    PlusToml = 5,
441    EndmatterYaml = 6,
442}
443
444unsafe extern "C" {
445    pub fn fig_editor_create(
446        input: *const u8,
447        input_len: usize,
448        format: c_int,
449        out_editor: *mut *mut FigEditor,
450    ) -> FigStatus;
451    pub fn fig_editor_destroy(editor: *mut FigEditor);
452
453    pub fn fig_editor_replace_val(
454        editor: *mut FigEditor,
455        path: *const FigPathSegment,
456        path_len: usize,
457        repl: *const u8,
458        repl_len: usize,
459    ) -> FigStatus;
460    pub fn fig_editor_replace_key(
461        editor: *mut FigEditor,
462        path: *const FigPathSegment,
463        path_len: usize,
464        repl: *const u8,
465        repl_len: usize,
466    ) -> FigStatus;
467    pub fn fig_editor_replace_named_key(
468        editor: *mut FigEditor,
469        path: *const FigPathSegment,
470        path_len: usize,
471        name: *const u8,
472        name_len: usize,
473    ) -> FigStatus;
474    pub fn fig_editor_set(
475        editor: *mut FigEditor,
476        path: *const FigPathSegment,
477        path_len: usize,
478        val: *const u8,
479        val_len: usize,
480    ) -> FigStatus;
481    pub fn fig_editor_add_leading_comment(
482        editor: *mut FigEditor,
483        path: *const FigPathSegment,
484        path_len: usize,
485        text: *const u8,
486        text_len: usize,
487    ) -> FigStatus;
488    pub fn fig_editor_set_trailing_comment(
489        editor: *mut FigEditor,
490        path: *const FigPathSegment,
491        path_len: usize,
492        text: *const u8,
493        text_len: usize,
494    ) -> FigStatus;
495    pub fn fig_editor_delete_leading_comments(
496        editor: *mut FigEditor,
497        path: *const FigPathSegment,
498        path_len: usize,
499    ) -> FigStatus;
500    pub fn fig_editor_delete_trailing_comment(
501        editor: *mut FigEditor,
502        path: *const FigPathSegment,
503        path_len: usize,
504    ) -> FigStatus;
505    pub fn fig_editor_get_leading_comment(
506        editor: *mut FigEditor,
507        path: *const FigPathSegment,
508        path_len: usize,
509        out_ptr: *mut *const u8,
510        out_len: *mut usize,
511    ) -> FigStatus;
512    pub fn fig_editor_get_trailing_comment(
513        editor: *mut FigEditor,
514        path: *const FigPathSegment,
515        path_len: usize,
516        out_ptr: *mut *const u8,
517        out_len: *mut usize,
518    ) -> FigStatus;
519    pub fn fig_editor_add_dangling_comment(
520        editor: *mut FigEditor,
521        path: *const FigPathSegment,
522        path_len: usize,
523        text: *const u8,
524        text_len: usize,
525    ) -> FigStatus;
526    pub fn fig_editor_delete_dangling_comments(
527        editor: *mut FigEditor,
528        path: *const FigPathSegment,
529        path_len: usize,
530    ) -> FigStatus;
531    pub fn fig_editor_get_dangling_comment(
532        editor: *mut FigEditor,
533        path: *const FigPathSegment,
534        path_len: usize,
535        out_ptr: *mut *const u8,
536        out_len: *mut usize,
537    ) -> FigStatus;
538    pub fn fig_editor_comment_out(
539        editor: *mut FigEditor,
540        path: *const FigPathSegment,
541        path_len: usize,
542    ) -> FigStatus;
543    pub fn fig_editor_uncomment_leading(
544        editor: *mut FigEditor,
545        path: *const FigPathSegment,
546        path_len: usize,
547        first_line: usize,
548        line_count: usize,
549    ) -> FigStatus;
550    pub fn fig_editor_uncomment_dangling(
551        editor: *mut FigEditor,
552        path: *const FigPathSegment,
553        path_len: usize,
554        first_line: usize,
555        line_count: usize,
556    ) -> FigStatus;
557    pub fn fig_editor_insert_key(
558        editor: *mut FigEditor,
559        path: *const FigPathSegment,
560        path_len: usize,
561        key: *const u8,
562        key_len: usize,
563        val: *const u8,
564        val_len: usize,
565    ) -> FigStatus;
566    pub fn fig_editor_insert_named_key(
567        editor: *mut FigEditor,
568        path: *const FigPathSegment,
569        path_len: usize,
570        name: *const u8,
571        name_len: usize,
572        val: *const u8,
573        val_len: usize,
574    ) -> FigStatus;
575    pub fn fig_editor_delete_key(
576        editor: *mut FigEditor,
577        path: *const FigPathSegment,
578        path_len: usize,
579    ) -> FigStatus;
580    pub fn fig_editor_append_seq(
581        editor: *mut FigEditor,
582        path: *const FigPathSegment,
583        path_len: usize,
584        val: *const u8,
585        val_len: usize,
586    ) -> FigStatus;
587    pub fn fig_editor_prepend_seq(
588        editor: *mut FigEditor,
589        path: *const FigPathSegment,
590        path_len: usize,
591        val: *const u8,
592        val_len: usize,
593    ) -> FigStatus;
594    pub fn fig_editor_remove_seq_item(
595        editor: *mut FigEditor,
596        path: *const FigPathSegment,
597        path_len: usize,
598        index: usize,
599    ) -> FigStatus;
600    pub fn fig_editor_move_key(
601        editor: *mut FigEditor,
602        src_path: *const FigPathSegment,
603        src_path_len: usize,
604        dest_path: *const FigPathSegment,
605        dest_path_len: usize,
606    ) -> FigStatus;
607    pub fn fig_editor_reorder_keys(
608        editor: *mut FigEditor,
609        path: *const FigPathSegment,
610        path_len: usize,
611        keys: *const FigStr,
612        keys_len: usize,
613    ) -> FigStatus;
614    pub fn fig_editor_move_item(
615        editor: *mut FigEditor,
616        path: *const FigPathSegment,
617        path_len: usize,
618        from: usize,
619        to: usize,
620    ) -> FigStatus;
621    pub fn fig_editor_reorder_items(
622        editor: *mut FigEditor,
623        path: *const FigPathSegment,
624        path_len: usize,
625        indices: *const usize,
626        indices_len: usize,
627    ) -> FigStatus;
628    pub fn fig_editor_set_sequence(
629        editor: *mut FigEditor,
630        path: *const FigPathSegment,
631        path_len: usize,
632        items: *const FigStr,
633        items_len: usize,
634    ) -> FigStatus;
635    // Whole-container ops, for containers scattered through the source (a TOML
636    // `[header]` table, an INI `[section]`, a fig block container). The key ops
637    // above cannot address one, and refuse with `INVALID_ARGUMENT` at such a
638    // path. A format that does not support the op answers `UNSUPPORTED_FORMAT`.
639    pub fn fig_editor_delete_container(
640        editor: *mut FigEditor,
641        path: *const FigPathSegment,
642        path_len: usize,
643    ) -> FigStatus;
644    pub fn fig_editor_insert_container(
645        editor: *mut FigEditor,
646        path: *const FigPathSegment,
647        path_len: usize,
648        body: *const u8,
649        body_len: usize,
650    ) -> FigStatus;
651    pub fn fig_editor_rename_container(
652        editor: *mut FigEditor,
653        path: *const FigPathSegment,
654        path_len: usize,
655        new_leaf: *const u8,
656        new_leaf_len: usize,
657    ) -> FigStatus;
658    /// A NULL `dest_path` means "to the end of the document" — distinct from a
659    /// zero-length path, which every other entry point reads as the root.
660    pub fn fig_editor_move_container(
661        editor: *mut FigEditor,
662        src_path: *const FigPathSegment,
663        src_path_len: usize,
664        dest_path: *const FigPathSegment,
665        dest_path_len: usize,
666    ) -> FigStatus;
667    pub fn fig_editor_reorder_containers(
668        editor: *mut FigEditor,
669        order: *const FigStr,
670        order_len: usize,
671    ) -> FigStatus;
672    pub fn fig_editor_append_container_to_seq(
673        editor: *mut FigEditor,
674        path: *const FigPathSegment,
675        path_len: usize,
676        body: *const u8,
677        body_len: usize,
678    ) -> FigStatus;
679    pub fn fig_editor_source(
680        editor: *const FigEditor,
681        out_ptr: *mut *const u8,
682        out_len: *mut usize,
683    ) -> FigStatus;
684
685    pub fn fig_embed_extract(
686        input: *const u8,
687        input_len: usize,
688        container: c_int,
689        format: c_int,
690        out_region: *mut FigRegion,
691    ) -> FigStatus;
692
693    pub fn fig_embed_detect(
694        input: *const u8,
695        input_len: usize,
696        out_container: *mut c_int,
697        out_format: *mut c_int,
698    ) -> FigStatus;
699
700    /// Re-house an embedded region under a different archetype's fences. On
701    /// `OK` the result is an OWNED buffer — release it with `fig_free`, passing
702    /// back the exact `out_len`. Added in core 2.7.0.
703    pub fn fig_embed_retype(
704        input: *const u8,
705        input_len: usize,
706        from_container: c_int,
707        from_format: c_int,
708        to_container: c_int,
709        to_format: c_int,
710        content: *const u8,
711        content_len: usize,
712        out_ptr: *mut *mut u8,
713        out_len: *mut usize,
714    ) -> FigStatus;
715
716    /// Sized free for a buffer fig allocated (`fig_embed_retype`'s result) or
717    /// that the caller obtained from `fig_alloc`. `len` must be exact.
718    pub fn fig_free(ptr: *mut u8, len: usize);
719
720    pub fn fig_embed_open(
721        input: *const u8,
722        input_len: usize,
723        container: c_int,
724        format: c_int,
725        out_embed: *mut *mut FigEmbed,
726    ) -> FigStatus;
727    pub fn fig_embed_open_or_init(
728        input: *const u8,
729        input_len: usize,
730        container: c_int,
731        format: c_int,
732        out_embed: *mut *mut FigEmbed,
733    ) -> FigStatus;
734    pub fn fig_embed_destroy(fm: *mut FigEmbed);
735
736    pub fn fig_embed_replace_val(
737        fm: *mut FigEmbed,
738        path: *const FigPathSegment,
739        path_len: usize,
740        repl: *const u8,
741        repl_len: usize,
742    ) -> FigStatus;
743    pub fn fig_embed_replace_key(
744        fm: *mut FigEmbed,
745        path: *const FigPathSegment,
746        path_len: usize,
747        repl: *const u8,
748        repl_len: usize,
749    ) -> FigStatus;
750    pub fn fig_embed_replace_named_key(
751        fm: *mut FigEmbed,
752        path: *const FigPathSegment,
753        path_len: usize,
754        name: *const u8,
755        name_len: usize,
756    ) -> FigStatus;
757    pub fn fig_embed_set(
758        fm: *mut FigEmbed,
759        path: *const FigPathSegment,
760        path_len: usize,
761        val: *const u8,
762        val_len: usize,
763    ) -> FigStatus;
764    pub fn fig_embed_add_leading_comment(
765        fm: *mut FigEmbed,
766        path: *const FigPathSegment,
767        path_len: usize,
768        text: *const u8,
769        text_len: usize,
770    ) -> FigStatus;
771    pub fn fig_embed_set_trailing_comment(
772        fm: *mut FigEmbed,
773        path: *const FigPathSegment,
774        path_len: usize,
775        text: *const u8,
776        text_len: usize,
777    ) -> FigStatus;
778    pub fn fig_embed_delete_leading_comments(
779        fm: *mut FigEmbed,
780        path: *const FigPathSegment,
781        path_len: usize,
782    ) -> FigStatus;
783    pub fn fig_embed_delete_trailing_comment(
784        fm: *mut FigEmbed,
785        path: *const FigPathSegment,
786        path_len: usize,
787    ) -> FigStatus;
788    pub fn fig_embed_get_leading_comment(
789        fm: *mut FigEmbed,
790        path: *const FigPathSegment,
791        path_len: usize,
792        out_ptr: *mut *const u8,
793        out_len: *mut usize,
794    ) -> FigStatus;
795    pub fn fig_embed_get_trailing_comment(
796        fm: *mut FigEmbed,
797        path: *const FigPathSegment,
798        path_len: usize,
799        out_ptr: *mut *const u8,
800        out_len: *mut usize,
801    ) -> FigStatus;
802    pub fn fig_embed_add_dangling_comment(
803        fm: *mut FigEmbed,
804        path: *const FigPathSegment,
805        path_len: usize,
806        text: *const u8,
807        text_len: usize,
808    ) -> FigStatus;
809    pub fn fig_embed_delete_dangling_comments(
810        fm: *mut FigEmbed,
811        path: *const FigPathSegment,
812        path_len: usize,
813    ) -> FigStatus;
814    pub fn fig_embed_get_dangling_comment(
815        fm: *mut FigEmbed,
816        path: *const FigPathSegment,
817        path_len: usize,
818        out_ptr: *mut *const u8,
819        out_len: *mut usize,
820    ) -> FigStatus;
821    pub fn fig_embed_comment_out(
822        fm: *mut FigEmbed,
823        path: *const FigPathSegment,
824        path_len: usize,
825    ) -> FigStatus;
826    pub fn fig_embed_uncomment_leading(
827        fm: *mut FigEmbed,
828        path: *const FigPathSegment,
829        path_len: usize,
830        first_line: usize,
831        line_count: usize,
832    ) -> FigStatus;
833    pub fn fig_embed_uncomment_dangling(
834        fm: *mut FigEmbed,
835        path: *const FigPathSegment,
836        path_len: usize,
837        first_line: usize,
838        line_count: usize,
839    ) -> FigStatus;
840    pub fn fig_embed_insert_key(
841        fm: *mut FigEmbed,
842        path: *const FigPathSegment,
843        path_len: usize,
844        key: *const u8,
845        key_len: usize,
846        val: *const u8,
847        val_len: usize,
848    ) -> FigStatus;
849    pub fn fig_embed_insert_named_key(
850        embed: *mut FigEmbed,
851        path: *const FigPathSegment,
852        path_len: usize,
853        name: *const u8,
854        name_len: usize,
855        val: *const u8,
856        val_len: usize,
857    ) -> FigStatus;
858    pub fn fig_embed_delete_key(
859        fm: *mut FigEmbed,
860        path: *const FigPathSegment,
861        path_len: usize,
862    ) -> FigStatus;
863    pub fn fig_embed_append_seq(
864        fm: *mut FigEmbed,
865        path: *const FigPathSegment,
866        path_len: usize,
867        val: *const u8,
868        val_len: usize,
869    ) -> FigStatus;
870    pub fn fig_embed_prepend_seq(
871        fm: *mut FigEmbed,
872        path: *const FigPathSegment,
873        path_len: usize,
874        val: *const u8,
875        val_len: usize,
876    ) -> FigStatus;
877    pub fn fig_embed_remove_seq_item(
878        fm: *mut FigEmbed,
879        path: *const FigPathSegment,
880        path_len: usize,
881        index: usize,
882    ) -> FigStatus;
883    pub fn fig_embed_move_key(
884        fm: *mut FigEmbed,
885        src_path: *const FigPathSegment,
886        src_path_len: usize,
887        dest_path: *const FigPathSegment,
888        dest_path_len: usize,
889    ) -> FigStatus;
890    pub fn fig_embed_reorder_keys(
891        fm: *mut FigEmbed,
892        path: *const FigPathSegment,
893        path_len: usize,
894        keys: *const FigStr,
895        keys_len: usize,
896    ) -> FigStatus;
897    pub fn fig_embed_move_item(
898        fm: *mut FigEmbed,
899        path: *const FigPathSegment,
900        path_len: usize,
901        from: usize,
902        to: usize,
903    ) -> FigStatus;
904    pub fn fig_embed_reorder_items(
905        fm: *mut FigEmbed,
906        path: *const FigPathSegment,
907        path_len: usize,
908        indices: *const usize,
909        indices_len: usize,
910    ) -> FigStatus;
911    pub fn fig_embed_set_sequence(
912        fm: *mut FigEmbed,
913        path: *const FigPathSegment,
914        path_len: usize,
915        items: *const FigStr,
916        items_len: usize,
917    ) -> FigStatus;
918    pub fn fig_embed_replace_body(fm: *mut FigEmbed, body: *const u8, body_len: usize)
919    -> FigStatus;
920    pub fn fig_embed_render(
921        fm: *mut FigEmbed,
922        out_ptr: *mut *const u8,
923        out_len: *mut usize,
924    ) -> FigStatus;
925}
926
927// ── Runtime languages ──────────────────────────────────────────────────────
928//
929// Mirrors of fig.h's "Runtime languages" section: the vtable a host fills to
930// register a language, and the node table its parse returns and its print
931// receives. Field for field with the header; `zig build abi-check` runs a
932// C-hosted language through the same structs, and the `fig` crate's tests run
933// a Rust-hosted one through these.
934
935/// Mirror of `FIG_LANGUAGE_VTABLE_VERSION`.
936pub const FIG_LANGUAGE_VTABLE_VERSION: u32 = 1;
937/// Mirror of `FIG_OFFSET_NONE` / `FIG_LEN_NONE`: an absent optional span or
938/// string.
939pub const FIG_OFFSET_NONE: usize = usize::MAX;
940pub const FIG_LEN_NONE: usize = usize::MAX;
941/// Mirror of `FIG_ROW_NONE`: the root's `parent`.
942pub const FIG_ROW_NONE: u32 = u32::MAX;
943/// Mirror of `FIG_EXT_NONE`.
944pub const FIG_EXT_NONE: c_int = -1;
945/// Mirror of `FIG_DEPTH_NONE`: a vtable's `max_mapping_depth` when unbounded.
946pub const FIG_DEPTH_NONE: c_int = -1;
947
948pub const FIG_MENTION_HEADER: c_int = 0;
949pub const FIG_MENTION_ENTRY: c_int = 1;
950pub const FIG_COMMENT_LEADING: c_int = 0;
951pub const FIG_COMMENT_TRAILING: c_int = 1;
952pub const FIG_COMMENT_DANGLING: c_int = 2;
953pub const FIG_COMMENT_LINE: c_int = 0;
954pub const FIG_COMMENT_BLOCK: c_int = 1;
955
956impl FigStr {
957    /// The absent optional string.
958    pub const NONE: FigStr = FigStr {
959        ptr: std::ptr::null(),
960        len: FIG_LEN_NONE,
961    };
962}
963
964impl FigSpan {
965    /// The absent optional span.
966    pub const NONE: FigSpan = FigSpan {
967        start: FIG_OFFSET_NONE,
968        end: FIG_OFFSET_NONE,
969    };
970}
971
972/// Mirror of `FigNodeRow`.
973#[repr(C)]
974#[derive(Clone, Copy, Debug)]
975pub struct FigNodeRow {
976    pub kind: c_int,
977    pub ext_kind: c_int,
978    pub parent: u32,
979    pub span: FigSpan,
980    pub text: FigStr,
981    pub anchor: FigStr,
982    pub anchor_span: FigSpan,
983    pub tag: FigStr,
984    pub tag_span: FigSpan,
985    pub marker: FigSpan,
986    pub sep: FigSpan,
987}
988
989/// Mirror of `FigRegionRow`.
990#[repr(C)]
991#[derive(Clone, Copy, Debug)]
992pub struct FigRegionRow {
993    pub node: u32,
994    pub start: usize,
995    pub end: usize,
996}
997
998/// Mirror of `FigMentionRow`.
999#[repr(C)]
1000#[derive(Clone, Copy, Debug)]
1001pub struct FigMentionRow {
1002    pub node: u32,
1003    pub span: FigSpan,
1004    pub kind: c_int,
1005}
1006
1007/// Mirror of `FigCommentRow`.
1008#[repr(C)]
1009#[derive(Clone, Copy, Debug)]
1010pub struct FigCommentRow {
1011    pub node: u32,
1012    pub slot: c_int,
1013    pub style: c_int,
1014    pub text: FigStr,
1015}
1016
1017/// Mirror of `FigDirectiveRow`.
1018#[repr(C)]
1019#[derive(Clone, Copy, Debug)]
1020pub struct FigDirectiveRow {
1021    pub handle: FigStr,
1022    pub prefix: FigStr,
1023}
1024
1025/// Mirror of `FigNodeTable`.
1026#[repr(C)]
1027#[derive(Clone, Copy, Debug)]
1028pub struct FigNodeTable {
1029    pub rows: *const FigNodeRow,
1030    pub row_count: usize,
1031    pub regions: *const FigRegionRow,
1032    pub region_count: usize,
1033    pub mentions: *const FigMentionRow,
1034    pub mention_count: usize,
1035    pub comments: *const FigCommentRow,
1036    pub comment_count: usize,
1037    pub directives: *const FigDirectiveRow,
1038    pub directive_count: usize,
1039    pub owner: *mut c_void,
1040}
1041
1042/// Mirror of `FigPrintOptions`.
1043#[repr(C)]
1044#[derive(Clone, Copy, Debug)]
1045pub struct FigPrintOptions {
1046    pub pretty: bool,
1047    pub strip_comments: bool,
1048    pub indent: u8,
1049    pub width: u16,
1050    pub splice: bool,
1051}
1052
1053/// Mirror of `FigCommentDelimiter`.
1054#[repr(C)]
1055#[derive(Clone, Copy, Debug)]
1056pub struct FigCommentDelimiter {
1057    pub open: *const c_char,
1058    pub close: *const c_char,
1059    pub forbidden: *const c_char,
1060}
1061
1062/// Mirror of `FigComments`.
1063#[repr(C)]
1064#[derive(Clone, Copy, Debug)]
1065pub struct FigComments {
1066    pub style: c_int,
1067    pub line: FigCommentDelimiter,
1068    pub trailing: FigCommentDelimiter,
1069}
1070
1071/// Mirror of `FigSectionHeader`.
1072#[repr(C)]
1073#[derive(Clone, Copy, Debug)]
1074pub struct FigSectionHeader {
1075    pub open: *const c_char,
1076    pub close: *const c_char,
1077    pub seq_open: *const c_char,
1078    pub seq_close: *const c_char,
1079    pub sep: *const c_char,
1080    pub skip_index: bool,
1081}
1082
1083/// Mirror of `FigClosedContainers`.
1084#[repr(C)]
1085#[derive(Clone, Copy, Debug)]
1086pub struct FigClosedContainers {
1087    pub map_open: *const c_char,
1088    pub map_close: *const c_char,
1089    pub seq_open: *const c_char,
1090    pub seq_close: *const c_char,
1091}
1092
1093/// Mirror of `FigSyntax`.
1094#[repr(C)]
1095#[derive(Clone, Copy, Debug)]
1096pub struct FigSyntax {
1097    pub comments: FigComments,
1098    pub kv_sep: *const c_char,
1099    pub flow_kv_sep_from_siblings: bool,
1100    pub flow_map_pad: *const c_char,
1101    pub key_style: c_int,
1102    pub key_sigil: u8,
1103    pub empty_map_literal: *const c_char,
1104    pub block_seq_editable: bool,
1105    pub flow_containers: bool,
1106    pub indent_unit: *const c_char,
1107    pub seq_item_marker: *const c_char,
1108    pub closed_containers: FigClosedContainers,
1109    pub single_line_block_mapping: bool,
1110    pub bare_document_mapping: bool,
1111    pub flow_map_open: *const c_char,
1112    pub flow_map_close: *const c_char,
1113    pub structural_indent: bool,
1114    pub section_noun: c_int,
1115    pub section_header: FigSectionHeader,
1116    pub merge_key: *const c_char,
1117}
1118
1119/// Mirror of `FigNativeKinds`.
1120#[repr(C)]
1121#[derive(Clone, Copy, Debug, Default)]
1122pub struct FigNativeKinds {
1123    pub null_: bool,
1124    pub offset_datetime: bool,
1125    pub local_datetime: bool,
1126    pub local_date: bool,
1127    pub local_time: bool,
1128    pub enum_literal: bool,
1129    pub char_literal: bool,
1130    pub number_special: bool,
1131    pub plist_date: bool,
1132    pub plist_data: bool,
1133}
1134
1135/// Mirror of `FigDialectDesc`.
1136#[repr(C)]
1137#[derive(Clone, Copy, Debug)]
1138pub struct FigDialectDesc {
1139    pub name: *const c_char,
1140    pub extensions: *const *const c_char,
1141    pub splice: c_int,
1142    pub empty_doc_seed: *const c_char,
1143    pub syntax: *const FigSyntax,
1144}
1145
1146pub type FigParseFn = unsafe extern "C" fn(
1147    ctx: *mut c_void,
1148    dialect: *const c_char,
1149    input: FigStr,
1150    out: *mut FigNodeTable,
1151    err: *mut FigError,
1152) -> c_int;
1153pub type FigPrintFn = unsafe extern "C" fn(
1154    ctx: *mut c_void,
1155    dialect: *const c_char,
1156    table: *const FigNodeTable,
1157    options: *const FigPrintOptions,
1158    out: *mut FigStr,
1159    err: *mut FigError,
1160) -> c_int;
1161pub type FigFreeTableFn = unsafe extern "C" fn(ctx: *mut c_void, table: *mut FigNodeTable);
1162pub type FigFreeBytesFn = unsafe extern "C" fn(ctx: *mut c_void, bytes: FigStr);
1163pub type FigRenderValueFn = unsafe extern "C" fn(
1164    ctx: *mut c_void,
1165    dialect: *const c_char,
1166    value: FigStr,
1167    literal: *const c_char,
1168    out: *mut FigStr,
1169    err: *mut FigError,
1170) -> c_int;
1171pub type FigRenderEntryFn = unsafe extern "C" fn(
1172    ctx: *mut c_void,
1173    dialect: *const c_char,
1174    indent: FigStr,
1175    key: FigStr,
1176    value: FigStr,
1177    out: *mut FigStr,
1178    err: *mut FigError,
1179) -> c_int;
1180pub type FigRenderItemFn = unsafe extern "C" fn(
1181    ctx: *mut c_void,
1182    dialect: *const c_char,
1183    indent: FigStr,
1184    value: FigStr,
1185    out: *mut FigStr,
1186    err: *mut FigError,
1187) -> c_int;
1188pub type FigRenderTailFn = FigRenderEntryFn;
1189pub type FigRenderKeyFn = FigRenderEntryFn;
1190
1191/// Mirror of `FigLanguageVTable`. The five `render_*` slots and `print` are
1192/// `Option`, which is the null pointer on the C side.
1193#[repr(C)]
1194#[derive(Clone, Copy)]
1195pub struct FigLanguageVTable {
1196    pub version: u32,
1197    pub ctx: *mut c_void,
1198    pub name: *const c_char,
1199    pub caps: u32,
1200    pub max_mapping_depth: c_int,
1201    pub lossless: *const FigNativeKinds,
1202    pub syntax: *const FigSyntax,
1203    pub dialects: *const FigDialectDesc,
1204    pub dialect_count: usize,
1205    pub samples: *const FigStr,
1206    pub sample_count: usize,
1207    pub parse: FigParseFn,
1208    pub print: Option<FigPrintFn>,
1209    pub free_table: FigFreeTableFn,
1210    pub free_bytes: FigFreeBytesFn,
1211    pub render_value: Option<FigRenderValueFn>,
1212    pub render_entry: Option<FigRenderEntryFn>,
1213    pub render_item: Option<FigRenderItemFn>,
1214    pub render_tail: Option<FigRenderTailFn>,
1215    pub render_key: Option<FigRenderKeyFn>,
1216}
1217
1218unsafe extern "C" {
1219    pub fn fig_language_vtable_version() -> u32;
1220    pub fn fig_language_register(
1221        vt: *const FigLanguageVTable,
1222        out_format: *mut c_int,
1223        out_err: *mut FigError,
1224    ) -> FigStatus;
1225    pub fn fig_format_by_name(name: *const c_char) -> c_int;
1226}