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 a higher-level crate (such as a future `libedit` crate) that
30//!    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#[cfg(feature = "bindgen")]
38include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
39
40#[cfg(not(feature = "bindgen"))]
41include!("bindings.rs");
42
43// Safety assertions at compile time
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use std::mem;
48
49    #[test]
50    fn test_editline_type_sizes() {
51        // EditLine, History, and Tokenizer are opaque pointers -- verify they compile
52        assert_eq!(mem::size_of::<*const EditLine>(), mem::size_of::<usize>());
53        assert_eq!(mem::size_of::<*const History>(), mem::size_of::<usize>());
54        assert_eq!(mem::size_of::<*const Tokenizer>(), mem::size_of::<usize>());
55    }
56
57    #[test]
58    fn test_file_used_only_behind_pointer() {
59        // `FILE` is deliberately an opaque, zero-sized type: libedit and this
60        // crate only ever use it as `*mut FILE`. A pointer to it must be a
61        // single machine word regardless of what libc's real `FILE` size is
62        // on the target platform. This locks in the "only used behind a
63        // pointer" contract so a future change that relies on the pointee's
64        // size (which would be platform-specific and unsound) can't slip in
65        // unnoticed.
66        assert_eq!(mem::size_of::<*mut FILE>(), mem::size_of::<usize>());
67        assert_eq!(
68            mem::size_of::<FILE>(),
69            0,
70            "FILE must remain zero-sized/opaque"
71        );
72    }
73
74    #[test]
75    fn test_hist_event() {
76        // HistEvent is a concrete struct, verify it exists
77        let _event: HistEvent = unsafe { mem::zeroed() };
78    }
79}