use crate::QuantumError;
use moonlab_sys::moonlab_control_server_t;
use std::ffi::CStr;
use std::os::raw::{c_char, c_int, c_void};
use std::sync::Arc;
pub struct AdmissionRequest<'a> {
tenant_id: Option<&'a str>,
verb: &'a str,
num_qubits: i32,
num_shots: i32,
}
impl<'a> AdmissionRequest<'a> {
pub fn tenant_id(&self) -> Option<&str> { self.tenant_id }
pub fn verb(&self) -> &str { self.verb }
pub fn num_qubits(&self) -> i32 { self.num_qubits }
pub fn num_shots(&self) -> i32 { self.num_shots }
}
pub enum AdmissionDecision {
Admitted,
Refused(i32),
}
type HookFn = dyn Fn(&AdmissionRequest) -> AdmissionDecision + Send + Sync + 'static;
pub struct AdmissionHook {
inner: Arc<HookFn>,
installed_on: std::cell::Cell<Option<*mut moonlab_control_server_t>>,
}
unsafe impl Send for AdmissionHook {}
unsafe impl Sync for AdmissionHook {}
impl AdmissionHook {
pub fn new<F>(f: F) -> Self
where
F: Fn(&AdmissionRequest) -> AdmissionDecision + Send + Sync + 'static,
{
AdmissionHook {
inner: Arc::new(f),
installed_on: std::cell::Cell::new(None),
}
}
pub unsafe fn install(
&self,
server: *mut moonlab_control_server_t,
) -> Result<(), QuantumError> {
if server.is_null() {
return Err(QuantumError::Ffi("install: server handle is null".into()));
}
let arc = Arc::clone(&self.inner);
let boxed: Box<Arc<HookFn>> = Box::new(arc);
let ctx = Box::into_raw(boxed) as *mut c_void;
let rc = unsafe {
moonlab_sys::moonlab_control_server_set_admission_hook(
server,
Some(trampoline),
ctx,
)
};
if rc != 0 {
let _ = unsafe { Box::from_raw(ctx as *mut Arc<HookFn>) };
return Err(QuantumError::Ffi(format!(
"set_admission_hook rc={rc}"
)));
}
self.installed_on.set(Some(server));
Ok(())
}
pub unsafe fn uninstall(
&self,
server: *mut moonlab_control_server_t,
) -> Result<(), QuantumError> {
let rc = unsafe {
moonlab_sys::moonlab_control_server_set_admission_hook(
server, None, std::ptr::null_mut(),
)
};
if rc != 0 {
return Err(QuantumError::Ffi(format!(
"set_admission_hook(NULL) rc={rc}"
)));
}
self.installed_on.set(None);
Ok(())
}
}
extern "C" fn trampoline(
tenant_id_p: *const c_char,
verb_p: *const c_char,
num_qubits: c_int,
num_shots: c_int,
ctx: *mut c_void,
) -> c_int {
if ctx.is_null() { return 0; }
let arc: &Arc<HookFn> = unsafe { &*(ctx as *const Arc<HookFn>) };
let hook: &HookFn = arc.as_ref();
let tenant_str: Option<&str> = if tenant_id_p.is_null() {
None
} else {
unsafe { CStr::from_ptr(tenant_id_p) }.to_str().ok()
};
let verb_str: &str = if verb_p.is_null() {
""
} else {
unsafe { CStr::from_ptr(verb_p) }.to_str().unwrap_or("")
};
let req = AdmissionRequest {
tenant_id: tenant_str,
verb: verb_str,
num_qubits: num_qubits as i32,
num_shots: num_shots as i32,
};
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
hook(&req)
}));
match result {
Ok(AdmissionDecision::Admitted) => 0,
Ok(AdmissionDecision::Refused(c)) => c,
Err(_) => -405, }
}