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}