use std::ffi::{CStr, CString};
use boxlite_shared::errors::{BoxliteError, BoxliteResult};
use super::config::GvproxyConfig;
use libgvproxy_sys::{
gvproxy_create, gvproxy_destroy, gvproxy_free_string, gvproxy_get_stats, gvproxy_get_version,
};
pub fn create_instance(config: &GvproxyConfig) -> BoxliteResult<i64> {
let json = serde_json::to_string(config)
.map_err(|e| BoxliteError::Network(format!("Failed to serialize config: {}", e)))?;
let c_json = CString::new(json)
.map_err(|e| BoxliteError::Network(format!("Invalid JSON string: {}", e)))?;
let id = unsafe { gvproxy_create(c_json.as_ptr()) };
if id < 0 {
return Err(BoxliteError::Network("gvproxy_create failed".to_string()));
}
tracing::info!(id, "Created gvproxy instance via FFI");
Ok(id)
}
pub fn destroy_instance(id: i64) -> BoxliteResult<()> {
let result = unsafe { gvproxy_destroy(id) };
if result != 0 {
return Err(BoxliteError::Network(format!(
"gvproxy_destroy failed for instance {}: code {}",
id, result
)));
}
tracing::info!(id, "Destroyed gvproxy instance via FFI");
Ok(())
}
pub fn get_version() -> BoxliteResult<String> {
let c_str = unsafe { gvproxy_get_version() };
if c_str.is_null() {
return Err(BoxliteError::Network(
"gvproxy_get_version returned NULL".to_string(),
));
}
let version = unsafe { CStr::from_ptr(c_str) }
.to_str()
.map_err(|e| BoxliteError::Network(format!("Invalid UTF-8 in version string: {}", e)))?
.to_string();
Ok(version)
}
pub fn get_stats_json(id: i64) -> BoxliteResult<String> {
let c_str = unsafe { gvproxy_get_stats(id) };
if c_str.is_null() {
return Err(BoxliteError::Network(format!(
"gvproxy_get_stats failed for instance {} (not found or not initialized)",
id
)));
}
let json_str = unsafe { CStr::from_ptr(c_str) }
.to_str()
.map_err(|e| BoxliteError::Network(format!("Invalid UTF-8 in stats JSON: {}", e)))?
.to_string();
unsafe { gvproxy_free_string(c_str) };
Ok(json_str)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn test_ffi_version() {
let version = get_version().unwrap();
assert!(!version.is_empty());
assert!(version.contains("gvproxy-bridge"));
}
#[test]
#[ignore] fn test_ffi_create_destroy() {
use std::path::PathBuf;
let socket_path = PathBuf::from("/tmp/test-gvproxy-ffi.sock");
let config = GvproxyConfig::new(socket_path, vec![(8080, 80), (8443, 443)]);
let id = create_instance(&config).unwrap();
destroy_instance(id).unwrap();
}
}