Skip to main content

ket/c_api/
mod.rs

1// SPDX-FileCopyrightText: 2026 Evandro Chagas Ribeiro da Rosa <evandro@quantuloop.com>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5#![allow(
6    clippy::missing_safety_doc,
7    clippy::manual_let_else,
8    clippy::not_unsafe_ptr_arg_deref,
9    clippy::macro_metavars_in_unsafe
10)]
11
12//! Foreign Function Interface (FFI) for the Libket library.
13//!
14//! This module provides a C-compatible API for the core data structures and
15//! execution mechanisms in Libket, enabling it to be consumed by other languages
16//! such as Python, C, and C++.
17//!
18//! # Design overview
19//!
20//! All opaque Rust types ([`crate::process::Process`],
21//! [`crate::ir::block::BasicBlock`], [`crate::process::QPUConfig`],
22//! [`crate::execution::QuantumExecution`]) are heap-allocated behind raw pointers.
23//! Callers are responsible for pairing every `*_new` call with the corresponding
24//! `*_delete` call to avoid memory leaks.
25//!
26//! Gate instructions and measurement results are exchanged as **JSON strings**
27//! (`const char *`). The caller owns any `char *` that Libket writes into an
28//! output pointer and **must** free it by calling [`ket_string_delete`] when
29//! done. Static C strings (e.g., the status returned by
30//! `ket_process_status`) are not heap-allocated and must **not** be freed.
31//!
32//! Every exported function returns a signed 32-bit integer error code. A
33//! return value of `0` indicates success. Non-zero values map to the
34//! [`KetError`] variants via [`KetError::from_error_code`]; call
35//! [`ket_error_message`] to retrieve a human-readable description.
36
37macro_rules! c_error {
38    ($ret:expr) => {
39        match $ret {
40            Ok(ret) => ret,
41            Err(err) => return err.error_code(),
42        }
43    };
44}
45
46macro_rules! to_json {
47    ($data:expr) => {
48        match serde_json::to_string($data) {
49            Ok(data) => {
50                if let Ok(data) = CString::new(data) {
51                    data
52                } else {
53                    return KetError::SerdeError.error_code();
54                }
55            }
56            Err(_) => return KetError::SerdeError.error_code(),
57        }
58    };
59}
60
61macro_rules! from_json {
62    ($data:expr, $ty:ty) => {{
63        let data = unsafe { std::ffi::CStr::from_ptr($data) };
64        let Ok(data) = data.to_str() else {
65            return KetError::SerdeError.error_code();
66        };
67        match serde_json::from_str::<$ty>(data) {
68            Ok(data) => data,
69            Err(_) => return KetError::SerdeError.error_code(),
70        }
71    }};
72}
73
74pub mod block;
75pub mod execute;
76pub mod process;
77
78use std::ffi::{c_char, CString};
79
80use crate::error::KetError;
81
82/// Retrieves the human-readable error message for a given integer error code.
83///
84/// On success the function heap-allocates a null-terminated C string and
85/// writes its address into `*error_message`. The caller must free this string
86/// with [`ket_string_delete`] when done.
87///
88/// # Returns
89/// `0` on success; a non-zero error code if the C string could not be
90/// allocated (e.g., if the message contains interior null bytes).
91#[no_mangle]
92pub extern "C" fn ket_error_message(error_code: i32, error_message: *mut *const c_char) -> i32 {
93    let Ok(msg) = CString::new(KetError::from_error_code(error_code).to_string()) else {
94        return 1;
95    };
96
97    unsafe {
98        *error_message = msg.into_raw();
99    }
100
101    c_ok()
102}
103
104/// Frees a heap-allocated C string that was previously written by Libket.
105///
106/// Every `const char **` output parameter written by Libket functions
107/// (`ket_error_message`, `ket_process_gates_json`, `ket_process_dump`, etc.)
108/// points to memory that must be released through this function. Passing a
109/// pointer that was not produced by Libket, or freeing the same pointer twice,
110/// is undefined behaviour.
111///
112/// # Returns
113/// Always returns `0`.
114#[no_mangle]
115pub extern "C" fn ket_string_delete(ptr: *mut c_char) -> i32 {
116    let _ = unsafe { CString::from_raw(ptr) };
117    c_ok()
118}
119
120/// Returns the success error code (`0`).
121#[inline(always)]
122pub(super) fn c_ok() -> i32 {
123    KetError::Success.error_code()
124}
125
126/// Converts a `Result<(), KetError>` to a C-compatible integer error code.
127///
128/// Returns `0` on `Ok(())`, or the discriminant of the `KetError` variant
129/// on `Err`. Suitable for wrapping infallible-looking calls that propagate
130/// errors through the return value.
131pub(super) fn c_error(error: Result<(), KetError>) -> i32 {
132    match error {
133        Ok(()) => c_ok(),
134        Err(error) => error.error_code(),
135    }
136}
137
138const BUILD_INFO: &str = build_info::format!("{} v{} [{} {}]", $.crate_info.name, $.crate_info.version, $.compiler, $.target);
139
140/// Returns a pointer and byte-length for a static build-information string.
141///
142/// The string is embedded at compile time and contains the crate name,
143/// version, Rust compiler version, and compilation target triple. It is
144/// **not** null-terminated; use `size` to bound reads. The pointer is valid
145/// for the lifetime of the program and must **not** be freed.
146///
147/// # Parameters
148/// - `msg` : Set to the address of the first byte of the build-info string.
149/// - `size`: Set to the byte length of the build-info string.
150///
151/// # Returns
152/// Always returns `0`.
153#[no_mangle]
154pub const extern "C" fn ket_build_info(msg: &mut *const u8, size: &mut usize) -> i32 {
155    let bytes = BUILD_INFO.as_bytes();
156    *size = bytes.len();
157    *msg = bytes.as_ptr();
158    KetError::Success.error_code()
159}