libxml_rs/abi/versioning.rs
1//! C ABI versioning — LIBXML2_VERSION, LIBXSLT_VERSION, runtime version APIs (§83, §84).
2//!
3//! This module implements the public C ABI version functions:
4//! - `xmlLibxmlVersion()` — returns LIBXML2_VERSION as integer
5//! - `xmlLibxmlVersionString()` — returns LIBXML2_VERSION string pointer
6//! - `xmlParserVersion()` — alias for xmlLibxmlVersionString
7//! - `xmlCheckVersion()` — runtime version compatibility check
8//! - `xsltLibxsltVersion()` — returns LIBXSLT_VERSION as integer
9//! - `xsltLibxsltVersionString()` — returns LIBXSLT_VERSION string pointer
10//! - `xsltCheckVersion()` — runtime XSLT version compatibility check
11//!
12//! # Phase 1 status
13//!
14//! Complete — all version APIs are implemented.
15//!
16//! # Compatibility profile
17//!
18//! Currently targeting libxml2 2.15.3 / libxslt 1.1.45 compatibility
19//! (the oracle toolchain on the reference system).
20//!
21//! # UPSTREAM-PARITY
22//!
23//! Upstream version format: major * 10000 + minor * 100 + micro
24//! Example: 2.15.3 → 21503
25
26#![allow(non_upper_case_globals)]
27
28use core::ffi::c_char;
29use core::sync::atomic::AtomicBool;
30use core::sync::atomic::Ordering;
31use std::os::raw::c_int;
32
33// ═══════════════════════════════════════════════════════════════════════════════
34// Target Version Constants
35// ═══════════════════════════════════════════════════════════════════════════════
36
37/// The target libxml2 version we aim to be compatible with.
38const TARGET_LIBXML2_MAJOR: c_int = 2;
39const TARGET_LIBXML2_MINOR: c_int = 15;
40const TARGET_LIBXML2_MICRO: c_int = 3;
41
42/// The target libxslt version we aim to be compatible with.
43const TARGET_LIBXSLT_MAJOR: c_int = 1;
44const TARGET_LIBXSLT_MINOR: c_int = 1;
45const TARGET_LIBXSLT_MICRO: c_int = 45;
46
47/// The version string for libxml2 compatibility.
48const LIBXML2_VERSION_STRING: &[u8; 7] = b"2.15.3\0";
49
50/// The version string for libxslt compatibility.
51const LIBXSLT_VERSION_STRING: &[u8; 7] = b"1.1.45\0";
52
53// ═══════════════════════════════════════════════════════════════════════════════
54// Version Macros (also defined in types.rs for compile-time use)
55// ═══════════════════════════════════════════════════════════════════════════════
56
57/// Compute the numeric version from major/minor/micro components.
58#[inline]
59pub const fn version_number(major: c_int, minor: c_int, micro: c_int) -> c_int {
60 major * 10000 + minor * 100 + micro
61}
62
63/// libxml2 version as a number: 2 * 10000 + 15 * 100 + 3 = 21503
64pub const LIBXML2_VERSION_NUM: c_int = version_number(
65 TARGET_LIBXML2_MAJOR,
66 TARGET_LIBXML2_MINOR,
67 TARGET_LIBXML2_MICRO,
68);
69
70/// libxslt version as a number: 1 * 10000 + 1 * 100 + 45 = 10145
71pub const LIBXSLT_VERSION_NUM: c_int = version_number(
72 TARGET_LIBXSLT_MAJOR,
73 TARGET_LIBXSLT_MINOR,
74 TARGET_LIBXSLT_MICRO,
75);
76
77// ═══════════════════════════════════════════════════════════════════════════════
78// Initialization Tracking
79// ═══════════════════════════════════════════════════════════════════════════════
80
81/// Whether the library has been initialized.
82static INITIALIZED: AtomicBool = AtomicBool::new(false);
83
84/// Mark the library as initialized.
85pub fn set_initialized() {
86 INITIALIZED.store(true, Ordering::Release);
87}
88
89/// Check whether the library has been initialized.
90pub fn is_initialized() -> bool {
91 INITIALIZED.load(Ordering::Acquire)
92}
93
94// ═══════════════════════════════════════════════════════════════════════════════
95// libxml2 Version Functions
96// ═══════════════════════════════════════════════════════════════════════════════
97
98/// Return the libxml2 version as an integer.
99///
100/// Returns `major * 10000 + minor * 100 + micro`.
101///
102/// # UPSTREAM-PARITY
103///
104/// ```c
105/// int xmlLibxmlVersion(void);
106/// ```
107///
108/// Oracle behavior (2.15.3): returns 21503.
109pub const fn xmlLibxmlVersion() -> c_int {
110 LIBXML2_VERSION_NUM
111}
112
113/// Return the libxml2 version as a static C string.
114///
115/// # UPSTREAM-PARITY
116///
117/// ```c
118/// const char *xmlLibxmlVersionString(void);
119/// ```
120///
121/// Oracle behavior (2.15.3): returns pointer to "2.15.3".
122pub const fn xmlLibxmlVersionString() -> *const c_char {
123 LIBXML2_VERSION_STRING.as_ptr() as *const c_char
124}
125
126/// Return the parser version string (alias for `xmlLibxmlVersionString`).
127///
128/// # UPSTREAM-PARITY
129///
130/// ```c
131/// const char *xmlParserVersion(void);
132/// ```
133pub const fn xmlParserVersion() -> *const c_char {
134 xmlLibxmlVersionString()
135}
136
137/// Check that the library version is at least `version`.
138///
139/// # Returns
140///
141/// - 0 if the library version is >= `version`
142/// - -1 if the library version is < `version`
143///
144/// # UPSTREAM-PARITY
145///
146/// ```c
147/// int xmlCheckVersion(int version);
148/// ```
149///
150/// Oracle behavior: compares LIBXML2_VERSION (compiled-in) against `version`.
151/// Returns 0 if compatible, -1 if not.
152///
153/// # SAFETY
154///
155/// The function touches crate-global state only; it is safe
156/// as long as the caller respects the library's global
157/// initialization/cleanup ordering (xmlInitParser before use,
158/// xmlCleanupParser only after all users are done).
159///
160/// Violating the global lifecycle ordering, or calling this after
161/// teardown or from a signal handler, is undefined behavior.
162#[no_mangle]
163pub const unsafe extern "C" fn xmlCheckVersion(version: c_int) -> c_int {
164 if LIBXML2_VERSION_NUM >= version {
165 0
166 } else {
167 -1
168 }
169}
170
171// ═══════════════════════════════════════════════════════════════════════════════
172// libxslt Version Functions
173// ═══════════════════════════════════════════════════════════════════════════════
174
175/// Return the libxslt version as an integer.
176///
177/// Returns `major * 10000 + minor * 100 + micro`.
178///
179/// # UPSTREAM-PARITY
180///
181/// ```c
182/// int xsltLibxsltVersion(void);
183/// ```
184pub const fn xsltLibxsltVersion() -> c_int {
185 LIBXSLT_VERSION_NUM
186}
187
188/// Return the libxslt version as a static C string.
189///
190/// # UPSTREAM-PARITY
191///
192/// ```c
193/// const char *xsltLibxsltVersionString(void);
194/// ```
195pub const fn xsltLibxsltVersionString() -> *const c_char {
196 LIBXSLT_VERSION_STRING.as_ptr() as *const c_char
197}
198
199/// Convert a C string pointer to a byte slice (NULL-safe).
200///
201/// # SAFETY
202///
203/// - `ptr` must be a valid null-terminated C string or NULL.
204pub unsafe fn c_str_to_bytes<'a>(ptr: *const c_char) -> Option<&'a [u8]> {
205 if ptr.is_null() {
206 return None;
207 }
208 let len = unsafe { libc::strlen(ptr) };
209 Some(unsafe { core::slice::from_raw_parts(ptr as *const u8, len) })
210}
211
212/// Check that the XSLT library version is at least `version`.
213///
214/// # Returns
215///
216/// - 0 if the library version is >= `version`
217/// - -1 if the library version is < `version`
218///
219/// # UPSTREAM-PARITY
220///
221/// ```c
222/// int xsltCheckVersion(int version);
223/// ```
224pub const fn xsltCheckVersion(version: c_int) -> c_int {
225 if LIBXSLT_VERSION_NUM >= version {
226 0
227 } else {
228 -1
229 }
230}
231
232// ═══════════════════════════════════════════════════════════════════════════════
233// Feature Detection
234// ═══════════════════════════════════════════════════════════════════════════════
235
236// ═══════════════════════════════════════════════════════════════════════════════
237// Compile-time Version Macros (for Rust consumers)
238// ═══════════════════════════════════════════════════════════════════════════════
239
240/// The libxml2 version as a number (compile-time constant).
241pub const LIBXML2_VERSION: c_int = LIBXML2_VERSION_NUM;
242
243/// The libxml2 version major number.
244pub const LIBXML2_VERSION_MAJOR: c_int = TARGET_LIBXML2_MAJOR;
245
246/// The libxml2 version minor number.
247pub const LIBXML2_VERSION_MINOR: c_int = TARGET_LIBXML2_MINOR;
248
249/// The libxml2 version micro number.
250pub const LIBXML2_VERSION_MICRO: c_int = TARGET_LIBXML2_MICRO;
251
252/// The libxml2 version as a number (alternate name).
253pub const LIBXML2_VERSION_NUMBER: c_int = LIBXML2_VERSION_NUM;
254
255/// Extra version suffix (empty string for release versions).
256pub const LIBXML2_VERSION_EXTRA: &[u8; 1] = b"\0";
257
258/// The libxslt version as a number (compile-time constant).
259pub const LIBXSLT_VERSION: c_int = LIBXSLT_VERSION_NUM;
260
261/// The libxslt version major number.
262pub const LIBXSLT_VERSION_MAJOR: c_int = TARGET_LIBXSLT_MAJOR;
263
264/// The libxslt version minor number.
265pub const LIBXSLT_VERSION_MINOR: c_int = TARGET_LIBXSLT_MINOR;
266
267/// The libxslt version micro number.
268pub const LIBXSLT_VERSION_MICRO: c_int = TARGET_LIBXSLT_MICRO;
269
270/// The libxslt version as a number (alternate name).
271pub const LIBXSLT_VERSION_NUMBER: c_int = LIBXSLT_VERSION_NUM;
272
273/// Extra version suffix for libxslt (empty string for release versions).
274pub const LIBXSLT_VERSION_EXTRA: &[u8; 1] = b"\0";
275
276// ═══════════════════════════════════════════════════════════════════════════════
277// Tests