feat: indexer client added to ffi

This commit is contained in:
Pravdyvy 2026-04-22 17:39:27 +03:00
parent ad6a55c55d
commit 9880a46bdc
7 changed files with 87 additions and 6 deletions

2
Cargo.lock generated
View File

@ -3485,7 +3485,9 @@ dependencies = [
"cbindgen",
"indexer_service",
"log",
"sequencer_core",
"tokio",
"url",
]
[[package]]

View File

@ -6,6 +6,9 @@ version = "0.1.0"
[dependencies]
indexer_service.workspace = true
sequencer_core.workspace = true
url.workspace = true
log = { workspace = true }
tokio = { features = ["rt-multi-thread"], workspace = true }

View File

@ -12,6 +12,7 @@ typedef enum OperationStatus {
typedef struct IndexerServiceFFI {
void *indexer_handle;
void *runtime;
void *indexer_client;
} IndexerServiceFFI;
/**

View File

@ -0,0 +1,36 @@
use std::net::SocketAddr;
use url::Url;
use crate::OperationStatus;
#[derive(Debug, Clone, Copy)]
pub enum UrlProtocol {
Http,
Ws,
}
impl std::fmt::Display for UrlProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Http => write!(f, "http"),
Self::Ws => write!(f, "ws"),
}
}
}
pub(crate) fn addr_to_url(protocol: UrlProtocol, addr: SocketAddr) -> Result<Url, OperationStatus> {
// Convert 0.0.0.0 to 127.0.0.1 for client connections
// When binding to port 0, the server binds to 0.0.0.0:<random_port>
// but clients need to connect to 127.0.0.1:<port> to work reliably
let url_string = if addr.ip().is_unspecified() {
format!("{protocol}://127.0.0.1:{}", addr.port())
} else {
format!("{protocol}://{addr}")
};
url_string.parse().map_err(|e| {
log::error!("Could not parse indexer url: {e}");
OperationStatus::InitializationError
})
}

View File

@ -1,8 +1,16 @@
use std::{ffi::c_char, path::PathBuf};
use sequencer_core::indexer_client::{IndexerClient, IndexerClientTrait};
use tokio::runtime::Runtime;
use crate::{IndexerServiceFFI, api::PointerResult, errors::OperationStatus};
use crate::{
IndexerServiceFFI,
api::{
PointerResult,
client::{UrlProtocol, addr_to_url},
},
errors::OperationStatus,
};
pub type InitializedIndexerServiceFFIResult = PointerResult<IndexerServiceFFI, OperationStatus>;
@ -67,7 +75,13 @@ fn setup_indexer(
OperationStatus::InitializationError
})?;
Ok(IndexerServiceFFI::new(indexer_handle, rt))
let indexer_url = addr_to_url(UrlProtocol::Ws, indexer_handle.addr())?;
let indexer_client = rt.block_on(IndexerClient::new(&indexer_url)).map_err(|e| {
log::error!("Could not start indexer client: {e}");
OperationStatus::InitializationError
})?;
Ok(IndexerServiceFFI::new(indexer_handle, rt, indexer_client))
}
/// Stops and frees the resources associated with the given indexer service.

View File

@ -1,5 +1,6 @@
pub use result::PointerResult;
pub mod client;
pub mod lifecycle;
pub mod memory;
pub mod result;

View File

@ -1,20 +1,27 @@
use std::{ffi::c_void, net::SocketAddr};
use indexer_service::IndexerHandle;
use sequencer_core::indexer_client::IndexerClient;
use tokio::runtime::Runtime;
#[repr(C)]
pub struct IndexerServiceFFI {
indexer_handle: *mut c_void,
runtime: *mut c_void,
indexer_client: *mut c_void,
}
impl IndexerServiceFFI {
pub fn new(indexer_handle: indexer_service::IndexerHandle, runtime: Runtime) -> Self {
pub fn new(
indexer_handle: indexer_service::IndexerHandle,
runtime: Runtime,
indexer_client: IndexerClient,
) -> Self {
Self {
// Box the complex types and convert to opaque pointers
indexer_handle: Box::into_raw(Box::new(indexer_handle)).cast::<c_void>(),
runtime: Box::into_raw(Box::new(runtime)).cast::<c_void>(),
indexer_client: Box::into_raw(Box::new(indexer_client)).cast::<c_void>(),
}
}
@ -25,10 +32,11 @@ impl IndexerServiceFFI {
/// The caller must ensure that:
/// - `self` is a valid object(contains valid pointers in all fields)
#[must_use]
pub unsafe fn into_parts(self) -> (Box<IndexerHandle>, Box<Runtime>) {
pub unsafe fn into_parts(self) -> (Box<IndexerHandle>, Box<Runtime>, Box<IndexerClient>) {
let indexer_handle = unsafe { Box::from_raw(self.indexer_handle.cast::<IndexerHandle>()) };
let runtime = unsafe { Box::from_raw(self.runtime.cast::<Runtime>()) };
(indexer_handle, runtime)
let indexer_client = unsafe { Box::from_raw(self.indexer_client.cast::<IndexerClient>()) };
(indexer_handle, runtime, indexer_client)
}
/// Helper to get indexer handle addr.
@ -49,7 +57,7 @@ impl IndexerServiceFFI {
indexer_handle.addr()
}
/// Helper to get indexer handle addr.
/// Helper to get indexer handle ref.
///
/// # Safety
///
@ -64,6 +72,22 @@ impl IndexerServiceFFI {
.expect("Indexer Handle must be non-null pointer")
}
}
/// Helper to get indexer client ref.
///
/// # Safety
///
/// The caller must ensure that:
/// - `self` is a valid object(contains valid pointers in all fields)
#[must_use]
pub const unsafe fn client(&self) -> &IndexerClient {
unsafe {
self.indexer_client
.cast::<IndexerClient>()
.as_ref()
.expect("Indexer Client must be non-null pointer")
}
}
}
// Implement Drop to prevent memory leaks