mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-06-28 11:59:31 +00:00
138 lines
5.0 KiB
Rust
138 lines
5.0 KiB
Rust
use std::{ffi::c_char, path::PathBuf};
|
|
|
|
use futures::StreamExt as _;
|
|
use indexer_core::{IndexerCore, config::IndexerConfig};
|
|
|
|
use crate::{IndexerServiceFFI, Runtime, api::PointerResult, errors::OperationStatus};
|
|
|
|
pub type InitializedIndexerServiceFFIResult = PointerResult<IndexerServiceFFI, OperationStatus>;
|
|
|
|
/// Creates and starts an indexer based on the provided
|
|
/// configuration file path.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// - `runtime`: A runtime for the indexer to run on, or null to have the indexer create and own
|
|
/// one.
|
|
/// - `config_path`: A pointer to a string representing the path to the configuration file.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// An `InitializedIndexerServiceFFIResult` containing either a pointer to the
|
|
/// initialized `IndexerServiceFFI` or an error code.
|
|
///
|
|
/// # Safety
|
|
/// The caller must ensure that:
|
|
/// - `runtime` is either null or a valid pointer to a [`Runtime`] that outlives the indexer.
|
|
/// - `config_path` is a valid pointer to a null-terminated C string.
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn start_indexer(
|
|
runtime: *const Runtime,
|
|
config_path: *const c_char,
|
|
) -> InitializedIndexerServiceFFIResult {
|
|
// SAFETY: The caller must ensure the validness of the `runtime` and `config_path` pointers.
|
|
unsafe { setup_indexer(runtime, config_path) }.map_or_else(
|
|
InitializedIndexerServiceFFIResult::from_error,
|
|
InitializedIndexerServiceFFIResult::from_value,
|
|
)
|
|
}
|
|
|
|
/// Initializes and starts an indexer based on the provided
|
|
/// configuration file path.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// - `runtime`: A runtime for the indexer to run on, or null to create and own one.
|
|
/// - `config_path`: A pointer to a string representing the path to the configuration file.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// A `Result` containing either the initialized `IndexerServiceFFI` or an
|
|
/// error code.
|
|
///
|
|
/// # Safety
|
|
/// The caller must ensure that:
|
|
/// - `runtime` is either null or a valid pointer to a [`Runtime`] that outlives the indexer.
|
|
/// - `config_path` is a valid pointer to a null-terminated C string.
|
|
unsafe fn setup_indexer(
|
|
runtime: *const Runtime,
|
|
config_path: *const c_char,
|
|
) -> Result<IndexerServiceFFI, OperationStatus> {
|
|
let user_config_path = PathBuf::from(
|
|
unsafe { std::ffi::CStr::from_ptr(config_path) }
|
|
.to_str()
|
|
.map_err(|e| {
|
|
log::error!("Could not convert the config path to string: {e}");
|
|
OperationStatus::InitializationError
|
|
})?,
|
|
);
|
|
let config = IndexerConfig::from_path(&user_config_path).map_err(|e| {
|
|
log::error!("Failed to read config: {e}");
|
|
OperationStatus::InitializationError
|
|
})?;
|
|
|
|
// Use the caller's runtime if one was supplied, otherwise create (and own)
|
|
// our own. The `Runtime` wrapper drops the underlying tokio runtime only
|
|
// when we own it; a borrowed one is left to its external owner.
|
|
let runtime = if runtime.is_null() {
|
|
Runtime::new().map_err(|e| {
|
|
log::error!("Could not create tokio runtime: {e}");
|
|
OperationStatus::InitializationError
|
|
})?
|
|
} else {
|
|
// SAFETY: the caller guarantees `runtime` is valid and outlives the indexer.
|
|
let caller = unsafe { &*runtime };
|
|
unsafe { Runtime::from_borrowed(caller.as_ref()) }
|
|
};
|
|
|
|
let core = IndexerCore::new(config).map_err(|e| {
|
|
log::error!("Could not initialize indexer core: {e}");
|
|
OperationStatus::InitializationError
|
|
})?;
|
|
|
|
// The block stream writes each parsed block into the store as a side effect
|
|
// of being polled, so we spawn a task that simply drains it. There are no
|
|
// subscribers — queries read the store directly via `core()`.
|
|
let ingest_core = core.clone();
|
|
let ingest_handle = runtime.spawn(async move {
|
|
let mut block_stream = std::pin::pin!(ingest_core.subscribe_parse_block_stream());
|
|
while let Some(result) = block_stream.next().await {
|
|
if let Err(e) = result {
|
|
log::error!("Indexer ingestion error: {e:#}");
|
|
}
|
|
}
|
|
log::warn!("Indexer block stream ended");
|
|
});
|
|
|
|
Ok(IndexerServiceFFI::new(core, ingest_handle, runtime))
|
|
}
|
|
|
|
/// Stops and frees the resources associated with the given indexer service.
|
|
///
|
|
/// # Arguments
|
|
///
|
|
/// - `indexer`: A pointer to the `IndexerServiceFFI` instance to be stopped.
|
|
///
|
|
/// # Returns
|
|
///
|
|
/// An `OperationStatus` indicating success or failure.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The caller must ensure that:
|
|
/// - `indexer` is a valid pointer to a `IndexerServiceFFI` instance
|
|
/// - The `IndexerServiceFFI` instance was created by this library
|
|
/// - The pointer will not be used after this function returns
|
|
#[unsafe(no_mangle)]
|
|
pub unsafe extern "C" fn stop_indexer(indexer: *mut IndexerServiceFFI) -> OperationStatus {
|
|
if indexer.is_null() {
|
|
log::error!("Attempted to stop a null indexer pointer. This is a bug. Aborting.");
|
|
return OperationStatus::NullPointer;
|
|
}
|
|
|
|
let indexer = unsafe { Box::from_raw(indexer) };
|
|
drop(indexer);
|
|
|
|
OperationStatus::Ok
|
|
}
|