Skip to main content

vips_sys/
lib.rs

1//! Low-level FFI bindings to `libvips`.
2//!
3//! By default this crate uses **pregenerated** bindings (`src/bindings/prebuilt.rs`)
4//! so consumers do not need clang/bindgen at build time.
5//!
6//! Enable the `bindgen` feature to regenerate bindings against the headers found
7//! on the machine (system libvips or `vendor/libvips`).
8//!
9//! # License
10//!
11//! This crate’s Rust source is **MIT**. The linked [`libvips`](https://www.libvips.org/)
12//! library is **LGPL-2.1** — distributing binaries that link libvips requires
13//! complying with LGPL-2.1 for that library. See the README “License” section.
14
15#![allow(non_upper_case_globals, non_camel_case_types, non_snake_case)]
16#![allow(clippy::missing_safety_doc)]
17
18/// Pregenerated bindings (default). Shared ABI for libvips 8.2+.
19#[cfg(not(feature = "bindgen"))]
20#[allow(clippy::all)]
21mod ffi {
22    include!("bindings/prebuilt.rs");
23}
24
25/// Live bindgen output from `OUT_DIR` (feature `bindgen`).
26#[cfg(feature = "bindgen")]
27#[allow(clippy::all)]
28mod ffi {
29    include!(concat!(env!("OUT_DIR"), "/binding.rs"));
30}
31
32pub use ffi::*;
33
34/// Optional minimal helpers for init / shutdown / version.
35#[cfg(feature = "helpers")]
36pub mod helpers {
37    use super::*;
38    use std::ffi::CString;
39    use std::sync::OnceLock;
40
41    /// Initialize libvips. Later calls reuse the first successful result.
42    pub fn init(argv0: &str) -> Result<(), i32> {
43        static INIT: OnceLock<Result<(), i32>> = OnceLock::new();
44        // Avoid CString allocation when init already succeeded.
45        if let Some(res) = INIT.get() {
46            return *res;
47        }
48        let c = CString::new(argv0).map_err(|_| -1)?;
49        *INIT.get_or_init(|| {
50            let rc = unsafe { vips_init(c.as_ptr()) };
51            if rc == 0 { Ok(()) } else { Err(rc) }
52        })
53    }
54
55    /// Shut down libvips. Prefer calling once at process exit.
56    pub fn shutdown() {
57        unsafe { vips_shutdown() }
58    }
59
60    /// `(major, minor, micro)` of the linked libvips. Cached after first call.
61    pub fn version() -> (i32, i32, i32) {
62        static VER: OnceLock<(i32, i32, i32)> = OnceLock::new();
63        *VER.get_or_init(|| unsafe { (vips_version(0), vips_version(1), vips_version(2)) })
64    }
65
66    /// `libvips` version string, e.g. `"8.18.6"`.
67    ///
68    /// The returned `&'static str` points at libvips-owned memory that lives
69    /// for the process lifetime.
70    pub fn version_string() -> &'static str {
71        static STR: OnceLock<&'static str> = OnceLock::new();
72        // Deref-coerce `&'static &'static str` → `&'static str` (no explicit `*`).
73        STR.get_or_init(|| unsafe {
74            let p = vips_version_string();
75            if p.is_null() {
76                ""
77            } else {
78                std::ffi::CStr::from_ptr(p).to_str().unwrap_or("")
79            }
80        })
81    }
82}
83
84#[cfg(all(test, feature = "helpers"))]
85mod tests {
86    use super::helpers;
87
88    #[test]
89    fn init_and_version() {
90        helpers::init("vips-sys-test").expect("vips_init");
91        let (maj, min, mic) = helpers::version();
92        assert!(maj >= 0 && min >= 0 && mic >= 0);
93        assert!(!helpers::version_string().is_empty());
94        // Cached: second call is free and consistent.
95        assert_eq!(helpers::version(), (maj, min, mic));
96    }
97}