Skip to main content

transcribe_cpp/
version.rs

1//! Version + ABI introspection and the load-time version gate.
2//!
3//! Pre-1.0 the on-disk ABI may break between minor releases, so the binding
4//! and the linked library must agree on the base `MAJOR.MINOR.PATCH`. The gate
5//! runs once, lazily, on the first model load (and is exposed directly so a
6//! host can check up front). Packaging-only suffixes on the runtime string are
7//! tolerated — only the leading release segment is compared.
8
9use std::sync::OnceLock;
10
11use transcribe_cpp_sys as sys;
12
13use crate::error::{Error, Result};
14use crate::result::owned_str;
15use crate::types::AbiStruct;
16
17/// The base version string this crate's generated FFI was built against.
18pub fn compiled_version() -> String {
19    format!(
20        "{}.{}.{}",
21        sys::TRANSCRIBE_VERSION_MAJOR,
22        sys::TRANSCRIBE_VERSION_MINOR,
23        sys::TRANSCRIBE_VERSION_PATCH
24    )
25}
26
27/// The `MAJOR.MINOR.PATCH` version string of the linked native library.
28pub fn version() -> String {
29    owned_str(unsafe { sys::transcribe_version() })
30}
31
32/// The short git commit the native library was built from (or "unknown").
33pub fn version_commit() -> String {
34    owned_str(unsafe { sys::transcribe_version_commit() })
35}
36
37/// The public-ABI digest the committed bindings were generated against.
38pub fn header_hash() -> &'static str {
39    sys::PUBLIC_HEADER_HASH
40}
41
42/// The native library's `sizeof` for a public ABI struct, or 0 if unknown.
43pub fn abi_struct_size(which: AbiStruct) -> usize {
44    unsafe { sys::transcribe_abi_struct_size(which.to_raw()) }
45}
46
47/// The native library's `alignof` for a public ABI struct, or 0 if unknown.
48pub fn abi_struct_align(which: AbiStruct) -> usize {
49    unsafe { sys::transcribe_abi_struct_align(which.to_raw()) }
50}
51
52/// Leading dotted-numeric release segment ("0.0.1.post1" -> "0.0.1").
53fn base(v: &str) -> &str {
54    let end = v
55        .find(|c: char| !(c.is_ascii_digit() || c == '.'))
56        .unwrap_or(v.len());
57    v[..end].trim_end_matches('.')
58}
59
60static GATE: OnceLock<std::result::Result<(), String>> = OnceLock::new();
61
62/// Run (once) the pre-1.0 base-version lock against the loaded library.
63pub(crate) fn ensure_compatible() -> Result<()> {
64    let outcome = GATE.get_or_init(|| {
65        let runtime = version();
66        let compiled = compiled_version();
67        if base(&runtime) == base(&compiled) {
68            Ok(())
69        } else {
70            Err(format!(
71                "loaded transcribe library is {runtime}, but these bindings \
72                 were generated for {compiled} (base versions must match \
73                 pre-1.0)"
74            ))
75        }
76    });
77    outcome.clone().map_err(Error::VersionMismatch)
78}