Skip to main content

libedit_sys/
lib.rs

1#![allow(non_upper_case_globals)]
2#![allow(non_camel_case_types)]
3#![allow(non_snake_case)]
4#![allow(dead_code)]
5#![allow(unknown_lints)]
6#![allow(unnecessary_transmutes)]
7#![allow(clippy::all)]
8
9//! Low-level FFI bindings to the [libedit](https://www.thrysoee.dk/editline/)
10//! line-editing library (also known as editline, the BSD alternative to GNU
11//! readline).
12//!
13//! These are **raw, unsafe** bindings generated by bindgen. They provide
14//! direct access to the C API with no ergonomic wrappers.
15//!
16//! # Variadic functions
17//!
18//! Several core functions are C variadic (`el_set`, `el_get`, `el_wset`,
19//! `el_wget`, `history`, `history_w`). Calling C variadic functions from
20//! Rust is **undefined behavior** and may silently corrupt data.
21//!
22//! *Simple integer/pointer arguments often work by accident*, but this is
23//! not guaranteed across platforms or compiler versions. To safely use the
24//! full libedit API, you have two options:
25//!
26//! 1. Write a small C shim that wraps specific variadic call patterns
27//!    with fixed-argument functions.
28//!
29//! 2. Use the companion [`libedit`](https://crates.io/crates/libedit)
30//!    crate, which provides safe wrappers and includes the necessary C shim.
31//!
32//! This crate intentionally keeps the raw variadic bindings available so
33//! advanced users are not restricted, but they are **use at your own risk**.
34
35// When the `bindgen` feature is enabled, use freshly generated bindings.
36// Otherwise, use the pre-generated bindings shipped with the crate
37// (platform-specific: macOS ships an older libedit with fewer symbols).
38#[cfg(feature = "bindgen")]
39include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
40
41#[cfg(all(not(feature = "bindgen"), target_os = "macos"))]
42include!("bindings-macos.rs");
43
44#[cfg(all(not(feature = "bindgen"), not(target_os = "macos")))]
45include!("bindings.rs");
46
47// Safety assertions at compile time
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use std::mem;
52
53    #[test]
54    fn test_editline_type_sizes() {
55        // EditLine, History, and Tokenizer are opaque pointers -- verify they compile
56        assert_eq!(mem::size_of::<*const EditLine>(), mem::size_of::<usize>());
57        assert_eq!(mem::size_of::<*const History>(), mem::size_of::<usize>());
58        assert_eq!(mem::size_of::<*const Tokenizer>(), mem::size_of::<usize>());
59    }
60
61    #[test]
62    fn test_file_used_only_behind_pointer() {
63        // `FILE` is deliberately an opaque, zero-sized type: libedit and this
64        // crate only ever use it as `*mut FILE`. A pointer to it must be a
65        // single machine word regardless of what libc's real `FILE` size is
66        // on the target platform. This locks in the "only used behind a
67        // pointer" contract so a future change that relies on the pointee's
68        // size (which would be platform-specific and unsound) can't slip in
69        // unnoticed.
70        assert_eq!(mem::size_of::<*mut FILE>(), mem::size_of::<usize>());
71        assert_eq!(
72            mem::size_of::<FILE>(),
73            0,
74            "FILE must remain zero-sized/opaque"
75        );
76    }
77
78    #[test]
79    fn test_hist_event() {
80        // HistEvent is a concrete struct, verify it exists
81        let _event: HistEvent = unsafe { mem::zeroed() };
82    }
83}