v0.1.0-pre.050

This commit is contained in:
2026-07-25 21:04:56 +02:00
parent 8286dc0919
commit 50fd7467f8
23 changed files with 3129 additions and 27 deletions

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/app_state.rs
// version: 3
// version: 4
//! Shared Tauri application state and startup initialization.
@@ -10,7 +10,7 @@ pub(crate) struct AppState {
active_profile: kb_config::ProfileConfig,
logging_guard: std::sync::Mutex<kb_logging::LoggingGuard>,
http_pool: kb_onchain_transport::HttpEndpointPool,
ws_pool: kb_onchain_transport::WsEndpointPool,
ws_pool: std::sync::Mutex<std::option::Option<std::sync::Arc<kb_onchain_transport::WsEndpointPool>>>,
demo_ws_session: tokio::sync::Mutex<std::option::Option<std::sync::Arc<kb_onchain_transport::WsSession>>>,
demo_backfill_running: std::sync::atomic::AtomicBool,
demo_backfill_cancel_requested: std::sync::atomic::AtomicBool,
@@ -37,17 +37,13 @@ impl crate::AppState {
std::result::Result::Ok(pool) => pool,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let ws_pool = match kb_onchain_transport::WsEndpointPool::from_profile(&active_profile) {
std::result::Result::Ok(pool) => pool,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(crate::AppState {
config_path: config_path.display().to_string(),
app_config,
active_profile,
logging_guard: std::sync::Mutex::new(logging_guard),
http_pool,
ws_pool,
ws_pool: std::sync::Mutex::new(std::option::Option::None),
demo_ws_session: tokio::sync::Mutex::new(std::option::Option::None),
demo_backfill_running: std::sync::atomic::AtomicBool::new(false),
demo_backfill_cancel_requested: std::sync::atomic::AtomicBool::new(false),
@@ -74,9 +70,28 @@ impl crate::AppState {
return &self.http_pool;
}
/// Returns the configured WebSocket endpoint pool.
pub(crate) fn ws_pool(&self) -> &kb_onchain_transport::WsEndpointPool {
return &self.ws_pool;
/// Returns the lazily initialized WebSocket endpoint pool.
pub(crate) fn demo_ws_pool(
&self,
) -> kb_core::Result<std::sync::Arc<kb_onchain_transport::WsEndpointPool>> {
let lock_result = self.ws_pool.lock();
let mut guard = match lock_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => {
return std::result::Result::Err(kb_core::Error::invalid_state(
"demo WebSocket pool lock is poisoned",
));
},
};
if let std::option::Option::Some(pool) = guard.as_ref() {
return std::result::Result::Ok(std::sync::Arc::clone(pool));
}
let pool = match kb_onchain_transport::WsEndpointPool::from_profile(&self.active_profile) {
std::result::Result::Ok(value) => std::sync::Arc::new(value),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
*guard = std::option::Option::Some(std::sync::Arc::clone(&pool));
return std::result::Result::Ok(pool);
}
/// Returns the persistent WebSocket demo session slot.

View File

@@ -0,0 +1,363 @@
// file: kb-app-demo-desktop/src/demo_sql_common.rs
// version: 8
//! Shared SQL demo helpers and serializable payloads.
use tauri::Manager; // rust-rules: trait-import
use ts_rs::TS; // rust-rules: derive-import
/// UI-safe table diagnostics shown by SQL demo windows.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql/DemoSqlTableSnapshot.ts"
)]
pub(crate) struct DemoSqlTableSnapshot {
/// Table name.
pub(crate) table_name: std::string::String,
/// Logical Solana domain encoded in the table name.
pub(crate) domain: std::string::String,
/// Human-readable table role.
pub(crate) role: std::string::String,
/// Whether the table currently exists.
pub(crate) exists: bool,
/// Row count when the table exists.
#[ts(type = "number | null")]
pub(crate) row_count: std::option::Option<i64>,
/// Minimum slot when available.
#[ts(type = "number | null")]
pub(crate) min_slot: std::option::Option<i64>,
/// Maximum slot when available.
#[ts(type = "number | null")]
pub(crate) max_slot: std::option::Option<i64>,
/// Latest insertion timestamp rendered by PostgreSQL when available.
pub(crate) latest_created_at: std::option::Option<std::string::String>,
}
/// Opens or focuses a SQL demo window.
pub(crate) fn open_sql_demo_window(
app_handle: tauri::AppHandle,
label: &str,
html_path: &str,
title: &str,
) -> std::result::Result<(), std::string::String> {
tracing::info!(target: crate::TRACING_TARGET, window = label, "open SQL demo window");
let existing_window = app_handle.get_webview_window(label);
if let std::option::Option::Some(window) = existing_window {
let show_result = window.show();
if let std::result::Result::Err(error) = show_result {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot show {label} window: {error}")).to_string(),
);
}
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus {label} window: {error}")).to_string(),
);
}
return std::result::Result::Ok(());
}
let builder = tauri::WebviewWindowBuilder::new(
&app_handle,
label,
tauri::WebviewUrl::App(html_path.into()),
)
.title(title)
.inner_size(1220.0, 780.0)
.min_inner_size(920.0, 560.0)
.resizable(true)
.visible(true);
let build_result = builder.build();
return match build_result {
std::result::Result::Ok(window) => {
let focus_result = window.set_focus();
if let std::result::Result::Err(error) = focus_result {
return std::result::Result::Err(
kb_core::Error::tauri(format!("cannot focus created {label} window: {error}"))
.to_string(),
);
}
std::result::Result::Ok(())
},
std::result::Result::Err(error) => std::result::Result::Err(
kb_core::Error::tauri(format!("cannot create {label} window: {error}")).to_string(),
),
};
}
/// Builds PostgreSQL options from the active profile without coupling kb-store to kb-config.
pub(crate) fn postgres_store_options_from_profile(
profile: &kb_config::ProfileConfig,
auto_initialize_schema: bool,
) -> kb_core::Result<kb_store::PostgresStoreOptions> {
return kb_store::PostgresStoreOptions::new(
profile.database.postgres.url.clone(),
profile.database.postgres.max_connections,
profile.database.postgres.connect_timeout_ms,
auto_initialize_schema,
);
}
/// Connects to PostgreSQL without reapplying schemas initialized at application startup.
pub(crate) async fn connect_postgres_store(
profile: &kb_config::ProfileConfig,
) -> std::result::Result<kb_store::PostgresStore, std::string::String> {
if !profile.database.enabled {
return std::result::Result::Err(std::string::String::from(
"database configuration is disabled in the active profile",
));
}
let options_result = postgres_store_options_from_profile(profile, false);
let mut options = match options_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
options.auto_initialize_schema = false;
let store_result = kb_store::PostgresStore::connect(options).await;
return match store_result {
std::result::Result::Ok(store) => std::result::Result::Ok(store),
std::result::Result::Err(error) => std::result::Result::Err(error.to_string()),
};
}
/// Converts store table diagnostics to the UI payload shape.
pub(crate) fn table_snapshot_from_pg(
value: &kb_store::PostgresTableDiagnostics,
) -> crate::DemoSqlTableSnapshot {
let row_count = match &value.statistics {
std::option::Option::Some(statistics) => std::option::Option::Some(statistics.row_count),
std::option::Option::None => std::option::Option::None,
};
let min_slot = match &value.statistics {
std::option::Option::Some(statistics) => statistics.min_slot,
std::option::Option::None => std::option::Option::None,
};
let max_slot = match &value.statistics {
std::option::Option::Some(statistics) => statistics.max_slot,
std::option::Option::None => std::option::Option::None,
};
let latest_created_at = match &value.statistics {
std::option::Option::Some(statistics) => statistics.latest_created_at.clone(),
std::option::Option::None => std::option::Option::None,
};
return crate::DemoSqlTableSnapshot {
table_name: value.table_name.clone(),
domain: value.domain.clone(),
role: value.role.clone(),
exists: value.exists,
row_count,
min_slot,
max_slot,
latest_created_at,
};
}
/// Converts many table diagnostics to UI payloads.
pub(crate) fn table_snapshots_from_pg(
values: &[kb_store::PostgresTableDiagnostics],
) -> std::vec::Vec<crate::DemoSqlTableSnapshot> {
let mut output = std::vec::Vec::new();
for value in values {
output.push(table_snapshot_from_pg(value));
}
return output;
}
/// Initializes PostgreSQL raw, core, decode and materialization schemas during Tauri startup.
pub(crate) async fn initialize_postgres_schema_for_startup(
state: &crate::AppState,
splash_window: &tauri::WebviewWindow,
) {
let profile = state.active_profile();
if !profile.database.enabled {
emit_sql_startup_splash(
splash_window,
"SQL store disabled; schema initialization skipped.",
"info",
false,
);
tracing::debug!(target: crate::TRACING_TARGET, "database is disabled; skip SQL schema initialization");
return;
}
if profile.database.backend != "postgres" {
emit_sql_startup_splash(
splash_window,
"SQL backend is not PostgreSQL; schema initialization skipped.",
"info",
false,
);
tracing::debug!(target: crate::TRACING_TARGET, backend = profile.database.backend.as_str(), "database backend is not postgres; skip SQL schema initialization");
return;
}
let options_result = postgres_store_options_from_profile(profile, false);
let mut options = match options_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
let error_message = error.to_string();
emit_sql_startup_error(splash_window, error_message.as_str());
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot build PostgreSQL store options");
return;
},
};
if !options.auto_initialize_schema {
emit_sql_startup_splash(
splash_window,
"PostgreSQL schema auto-initialization disabled.",
"info",
false,
);
let masked_dsn = options.masked_dsn();
tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "PostgreSQL schema auto-initialization disabled");
return;
}
let masked_dsn = options.masked_dsn();
options.auto_initialize_schema = false;
emit_sql_startup_splash(
splash_window,
"Checking PostgreSQL raw/core/decode tables...",
"info",
true,
);
tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "start PostgreSQL schema initialization");
let store_result = kb_store::PostgresStore::connect(options).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
let error_message = error.to_string();
emit_sql_startup_error(splash_window, error_message.as_str());
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL connection failed during startup schema initialization");
return;
},
};
let before_result = store.known_table_diagnostics().await;
let before_tables = match before_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
let error_message = error.to_string();
emit_sql_startup_error(splash_window, error_message.as_str());
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot read table diagnostics before schema initialization");
return;
},
};
let initialize_result = store.initialize_store_schema().await;
if let std::result::Result::Err(error) = initialize_result {
let error_message = error.to_string();
emit_sql_startup_error(splash_window, error_message.as_str());
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL store schema initialization failed");
return;
}
let after_result = store.known_table_diagnostics().await;
let after_tables = match after_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
let error_message = error.to_string();
emit_sql_startup_error(splash_window, error_message.as_str());
tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot read table diagnostics after schema initialization");
return;
},
};
emit_sql_startup_table_report(splash_window, before_tables.as_slice(), after_tables.as_slice());
}
fn emit_sql_startup_table_report(
splash_window: &tauri::WebviewWindow,
before_tables: &[kb_store::PostgresTableDiagnostics],
after_tables: &[kb_store::PostgresTableDiagnostics],
) {
let mut created_count = 0_u32;
let mut existing_count = 0_u32;
let mut missing_count = 0_u32;
for table in after_tables {
let before_exists = table_exists_in(before_tables, table.table_name.as_str());
if table.exists && before_exists == std::option::Option::Some(false) {
created_count += 1;
emit_sql_startup_splash(
splash_window,
format!("Created table {}", table.table_name).as_str(),
"success",
true,
);
tracing::debug!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table created");
} else if table.exists {
existing_count += 1;
emit_sql_startup_splash(
splash_window,
format!("Table {} already exists", table.table_name).as_str(),
"info",
true,
);
tracing::debug!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table already exists");
} else {
missing_count += 1;
emit_sql_startup_splash(
splash_window,
format!("Table {} is missing after initialization", table.table_name).as_str(),
"warning",
true,
);
tracing::error!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table missing after initialization");
}
}
emit_sql_startup_splash(
splash_window,
format!(
"PostgreSQL schema ready: {created_count} created, {existing_count} already present, {missing_count} missing."
)
.as_str(),
schema_report_status(missing_count),
false,
);
tracing::debug!(target: crate::TRACING_TARGET, created = created_count, existing = existing_count, missing = missing_count, "PostgreSQL schema initialization completed");
}
fn table_exists_in(
tables: &[kb_store::PostgresTableDiagnostics],
table_name: &str,
) -> std::option::Option<bool> {
for table in tables {
if table.table_name.as_str() == table_name {
return std::option::Option::Some(table.exists);
}
}
return std::option::Option::None;
}
fn schema_report_status(missing_count: u32) -> &'static str {
if missing_count == 0 {
return "success";
}
return "warning";
}
fn emit_sql_startup_error(splash_window: &tauri::WebviewWindow, message: &str) {
emit_sql_startup_splash(
splash_window,
format!("PostgreSQL schema initialization error: {message}").as_str(),
"danger",
false,
);
}
fn emit_sql_startup_splash(
splash_window: &tauri::WebviewWindow,
message: &str,
status: &str,
log_only: bool,
) {
let order = if log_only { "add_log" } else { "add_msg" };
crate::emit_splash_order(
splash_window,
order,
std::option::Option::Some(message),
std::option::Option::Some(status),
std::option::Option::None,
);
}
/// Formats debug enums for UI display.
pub(crate) fn debug_status<T: std::fmt::Debug>(value: T) -> std::string::String {
return format!("{value:?}");
}

View File

@@ -0,0 +1,38 @@
// file: kb-app-demo-desktop/src/demo_sql_diag.rs
// version: 3
//! SQL diagnostic demo commands.
use ts_rs::TS; // rust-rules: derive-import
/// Complete SQL diagnostic payload shown by `demo_sql_diag`.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_diag/DemoSqlDiagPayload.ts"
)]
pub(crate) struct DemoSqlDiagPayload {
/// Configuration file path.
pub(crate) config_path: std::string::String,
/// Active profile name.
pub(crate) active_profile_name: std::string::String,
/// Configured database backend.
pub(crate) backend: std::string::String,
/// Masked PostgreSQL DSN.
pub(crate) masked_dsn: std::string::String,
/// PostgreSQL current schema.
pub(crate) current_schema: std::option::Option<std::string::String>,
/// PostgreSQL health status.
pub(crate) health_status: std::string::String,
/// PostgreSQL health message.
pub(crate) health_message: std::option::Option<std::string::String>,
/// Migration status.
pub(crate) migration_status: std::string::String,
/// Migration diagnostic message.
pub(crate) migration_message: std::option::Option<std::string::String>,
/// PostgreSQL server version when available.
pub(crate) server_version: std::option::Option<std::string::String>,
/// Known raw/core table diagnostics.
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
}

View File

@@ -0,0 +1,22 @@
// file: kb-app-demo-desktop/src/demo_sql_pg_core.rs
// version: 4
//! PostgreSQL core store demo commands.
use ts_rs::TS; // rust-rules: derive-import
/// PostgreSQL core store payload shown by `demo_sql_pg_core`.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_pg_core/DemoSqlPgCorePayload.ts"
)]
pub(crate) struct DemoSqlPgCorePayload {
/// Active profile name.
pub(crate) active_profile_name: std::string::String,
/// Masked PostgreSQL DSN.
pub(crate) masked_dsn: std::string::String,
/// Core table diagnostics.
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
}

View File

@@ -0,0 +1,22 @@
// file: kb-app-demo-desktop/src/demo_sql_pg_raw.rs
// version: 5
//! PostgreSQL canonical acquisition store demo commands.
use ts_rs::TS; // rust-rules: derive-import
/// PostgreSQL canonical acquisition payload shown by `demo_sql_pg_raw`.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_pg_raw/DemoSqlPgRawPayload.ts"
)]
pub(crate) struct DemoSqlPgRawPayload {
/// Active profile name.
pub(crate) active_profile_name: std::string::String,
/// Masked PostgreSQL DSN.
pub(crate) masked_dsn: std::string::String,
/// Raw table diagnostics.
pub(crate) tables: std::vec::Vec<crate::DemoSqlTableSnapshot>,
}

View File

@@ -0,0 +1,409 @@
// file: kb-app-demo-desktop/src/demo_sql_replay_candidates.rs
// version: 7
//! Read-only SQL replay candidate browser commands.
use ts_rs::TS; // rust-rules: derive-import
/// One program identifier exposed by the runtime `kb_program_ids` registry.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayKnownProgramOption.ts"
)]
pub(crate) struct DemoSqlReplayKnownProgramOption {
/// Stable lower snake case program code.
pub(crate) code: std::string::String,
/// Base58 Solana program identifier.
pub(crate) program_id: std::string::String,
}
/// Static options for the replay candidate browser.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayOptionsPayload.ts"
)]
pub(crate) struct DemoSqlReplayOptionsPayload {
/// Active profile name.
pub(crate) active_profile_name: std::string::String,
/// Masked PostgreSQL DSN.
pub(crate) masked_dsn: std::string::String,
/// Maximum rows accepted by one query.
pub(crate) maximum_limit: u32,
/// Program identifiers enumerable from `kb_program_ids`.
pub(crate) known_programs: std::vec::Vec<crate::DemoSqlReplayKnownProgramOption>,
}
/// UI request for bounded transaction replay candidates.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayTransactionRequest.ts"
)]
pub(crate) struct DemoSqlReplayTransactionRequest {
/// Optional partial signature search.
pub(crate) signature_contains: std::option::Option<std::string::String>,
/// Optional inclusive minimum slot.
#[ts(type = "number | null")]
pub(crate) min_slot: std::option::Option<u64>,
/// Optional inclusive maximum slot.
#[ts(type = "number | null")]
pub(crate) max_slot: std::option::Option<u64>,
/// Optional raw processing state.
pub(crate) raw_processing_state: std::option::Option<std::string::String>,
/// Optional latest ledger status.
pub(crate) ledger_status: std::option::Option<std::string::String>,
/// Optional exact program id.
pub(crate) program_id: std::option::Option<std::string::String>,
/// Program scope code: any, outer, inner or logs.
pub(crate) program_scope: std::string::String,
/// Optional entity kind: mint, owner or account_key.
pub(crate) entity_kind: std::option::Option<std::string::String>,
/// Optional exact entity value.
pub(crate) entity_value: std::option::Option<std::string::String>,
/// Maximum returned rows.
pub(crate) limit: u32,
/// Orders newest slots first when true.
pub(crate) newest_first: bool,
}
/// One transaction row shown by the replay candidate browser.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayTransactionRow.ts"
)]
pub(crate) struct DemoSqlReplayTransactionRow {
/// Canonical transaction signature.
pub(crate) signature: std::string::String,
/// Transaction slot.
#[ts(type = "number")]
pub(crate) slot: i64,
/// Current raw processing state.
pub(crate) raw_processing_state: std::string::String,
/// Current raw retention state.
pub(crate) retention_state: std::string::String,
/// Whether a core transaction exists.
pub(crate) has_core_transaction: bool,
/// Core transaction failure flag when available.
pub(crate) transaction_failed: std::option::Option<bool>,
/// Latest core extraction ledger status.
pub(crate) ledger_status: std::string::String,
/// Latest processor version when available.
pub(crate) processor_version: std::option::Option<std::string::String>,
/// Latest attempt count.
pub(crate) attempt_count: i32,
/// Number of top-level instructions.
#[ts(type = "number")]
pub(crate) outer_instruction_count: i64,
/// Number of inner instructions.
#[ts(type = "number")]
pub(crate) inner_instruction_count: i64,
/// Number of distinct top-level programs.
#[ts(type = "number")]
pub(crate) outer_program_count: i64,
/// Number of distinct inner programs.
#[ts(type = "number")]
pub(crate) inner_program_count: i64,
/// Raw row update timestamp.
pub(crate) updated_at: std::string::String,
}
/// UI request for bounded program summaries.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayProgramRequest.ts"
)]
pub(crate) struct DemoSqlReplayProgramRequest {
/// Optional partial program id search.
pub(crate) program_id_contains: std::option::Option<std::string::String>,
/// Maximum returned rows.
pub(crate) limit: u32,
}
/// Aggregated program row shown by the replay candidate browser.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayProgramRow.ts"
)]
pub(crate) struct DemoSqlReplayProgramRow {
/// Optional stable code from `kb_program_ids`.
pub(crate) program_code: std::option::Option<std::string::String>,
/// Program id.
pub(crate) program_id: std::string::String,
/// Number of distinct transactions.
#[ts(type = "number")]
pub(crate) transaction_count: i64,
/// Number of top-level instruction occurrences.
#[ts(type = "number")]
pub(crate) outer_instruction_count: i64,
/// Number of inner instruction occurrences.
#[ts(type = "number")]
pub(crate) inner_instruction_count: i64,
/// Number of reliably linked log occurrences.
#[ts(type = "number")]
pub(crate) log_count: i64,
/// Lowest observed slot.
#[ts(type = "number")]
pub(crate) min_slot: i64,
/// Highest observed slot.
#[ts(type = "number")]
pub(crate) max_slot: i64,
}
/// UI request for bounded core entity summaries.
#[derive(Clone, Debug, serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayEntityRequest.ts"
)]
pub(crate) struct DemoSqlReplayEntityRequest {
/// Entity kind: mint, owner or account_key.
pub(crate) entity_kind: std::string::String,
/// Optional partial entity value search.
pub(crate) entity_value_contains: std::option::Option<std::string::String>,
/// Maximum returned rows.
pub(crate) limit: u32,
}
/// Aggregated core entity row shown by the replay candidate browser.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(
export,
export_to = "../frontend/ts/bindings/kb_app_demo_desktop/demo_sql_replay_candidates/DemoSqlReplayEntityRow.ts"
)]
pub(crate) struct DemoSqlReplayEntityRow {
/// Stable entity kind code.
pub(crate) entity_kind: std::string::String,
/// Mint, owner or account-key address.
pub(crate) entity_value: std::string::String,
/// Number of distinct transactions.
#[ts(type = "number")]
pub(crate) transaction_count: i64,
/// Total number of core-table occurrences.
#[ts(type = "number")]
pub(crate) occurrence_count: i64,
/// Lowest observed slot.
#[ts(type = "number")]
pub(crate) min_slot: i64,
/// Highest observed slot.
#[ts(type = "number")]
pub(crate) max_slot: i64,
}
pub(crate) fn transaction_row_from_pg(
row: kb_store::PostgresReplayTransactionCandidate,
) -> crate::DemoSqlReplayTransactionRow {
return crate::DemoSqlReplayTransactionRow {
signature: row.signature,
slot: row.slot,
raw_processing_state: row.raw_processing_state,
retention_state: row.retention_state,
has_core_transaction: row.has_core_transaction,
transaction_failed: row.transaction_failed,
ledger_status: row.ledger_status,
processor_version: row.processor_version,
attempt_count: row.attempt_count,
outer_instruction_count: row.outer_instruction_count,
inner_instruction_count: row.inner_instruction_count,
outer_program_count: row.outer_program_count,
inner_program_count: row.inner_program_count,
updated_at: row.updated_at,
};
}
pub(crate) fn program_row_from_pg(
row: kb_store::PostgresReplayProgramSummary,
) -> crate::DemoSqlReplayProgramRow {
let program_code = kb_program_ids::find_registered_program_id(&row.program_id)
.map(|entry| return entry.code().to_owned());
return crate::DemoSqlReplayProgramRow {
program_code,
program_id: row.program_id,
transaction_count: row.transaction_count,
outer_instruction_count: row.outer_instruction_count,
inner_instruction_count: row.inner_instruction_count,
log_count: row.log_count,
min_slot: row.min_slot,
max_slot: row.max_slot,
};
}
pub(crate) fn entity_row_from_pg(
row: kb_store::PostgresReplayEntitySummary,
) -> crate::DemoSqlReplayEntityRow {
return crate::DemoSqlReplayEntityRow {
entity_kind: row.entity_kind,
entity_value: row.entity_value,
transaction_count: row.transaction_count,
occurrence_count: row.occurrence_count,
min_slot: row.min_slot,
max_slot: row.max_slot,
};
}
pub(crate) fn validated_csv_file_name(
file_name: &str,
) -> std::result::Result<&'static str, std::string::String> {
return match file_name {
"replay_transactions.csv" => std::result::Result::Ok("replay_transactions.csv"),
"replay_programs.csv" => std::result::Result::Ok("replay_programs.csv"),
"replay_mints.csv" => std::result::Result::Ok("replay_mints.csv"),
"replay_owners.csv" => std::result::Result::Ok("replay_owners.csv"),
"replay_account_keys.csv" => std::result::Result::Ok("replay_account_keys.csv"),
_ => std::result::Result::Err(format!("unsupported replay CSV file name: {file_name}")),
};
}
pub(crate) fn csv_export_directory_from_current_dir(
current_dir: &std::path::Path,
) -> std::path::PathBuf {
let current_name = current_dir.file_name().and_then(std::ffi::OsStr::to_str);
if let (std::option::Option::Some("kb_app_demo_desktop"), std::option::Option::Some(parent)) =
(current_name, current_dir.parent())
{
return parent.join("data").join("exports_csv");
}
return current_dir.join("data").join("exports_csv");
}
pub(crate) async fn available_csv_export_path(
export_dir: &std::path::Path,
file_name: &str,
) -> std::result::Result<std::path::PathBuf, std::string::String> {
let direct_path = export_dir.join(file_name);
let direct_exists_result = tokio::fs::try_exists(&direct_path).await;
let direct_exists = match direct_exists_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("cannot inspect CSV export path: {error}"));
},
};
if !direct_exists {
return std::result::Result::Ok(direct_path);
}
let stem = file_name.trim_end_matches(".csv");
for suffix in 1_u16..=999_u16 {
let candidate = export_dir.join(format!("{stem}_{suffix}.csv"));
let exists_result = tokio::fs::try_exists(&candidate).await;
let exists = match exists_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!(
"cannot inspect CSV export path: {error}"
));
},
};
if !exists {
return std::result::Result::Ok(candidate);
}
}
return std::result::Result::Err("cannot allocate a unique CSV export path".to_owned());
}
pub(crate) fn program_scope_from_code(
code: &str,
) -> std::result::Result<kb_store::PostgresReplayProgramScope, std::string::String> {
return match code {
"any" => std::result::Result::Ok(kb_store::PostgresReplayProgramScope::Any),
"outer" => std::result::Result::Ok(kb_store::PostgresReplayProgramScope::Outer),
"inner" => std::result::Result::Ok(kb_store::PostgresReplayProgramScope::Inner),
"logs" => std::result::Result::Ok(kb_store::PostgresReplayProgramScope::Logs),
_ => std::result::Result::Err(format!("unsupported replay program scope: {code}")),
};
}
pub(crate) fn optional_entity_kind_from_code(
code: std::option::Option<&str>,
) -> std::result::Result<
std::option::Option<kb_store::PostgresReplayEntityKind>,
std::string::String,
> {
return match code {
std::option::Option::Some(value) => {
let result = entity_kind_from_code(value);
match result {
std::result::Result::Ok(kind) => {
std::result::Result::Ok(std::option::Option::Some(kind))
},
std::result::Result::Err(error) => std::result::Result::Err(error),
}
},
std::option::Option::None => std::result::Result::Ok(std::option::Option::None),
};
}
pub(crate) fn entity_kind_from_code(
code: &str,
) -> std::result::Result<kb_store::PostgresReplayEntityKind, std::string::String> {
return match code {
"mint" => std::result::Result::Ok(kb_store::PostgresReplayEntityKind::Mint),
"owner" => std::result::Result::Ok(kb_store::PostgresReplayEntityKind::Owner),
"account_key" => std::result::Result::Ok(kb_store::PostgresReplayEntityKind::AccountKey),
_ => std::result::Result::Err(format!("unsupported replay entity kind: {code}")),
};
}
#[cfg(test)]
mod tests {
#[test]
fn program_scope_parser_accepts_logs() {
let result = crate::program_scope_from_code("logs");
assert_eq!(result, std::result::Result::Ok(kb_store::PostgresReplayProgramScope::Logs));
}
#[test]
fn entity_kind_parser_rejects_unknown_code() {
let result = crate::entity_kind_from_code("unknown");
assert!(result.is_err());
}
#[test]
fn csv_file_name_rejects_unknown_name() {
let result = crate::validated_csv_file_name("arbitrary.csv");
assert!(result.is_err());
}
#[test]
fn csv_export_directory_uses_workspace_root_from_demo_crate() {
let current_dir = std::path::Path::new("/tmp/khadhroony-bot2/kb_app_demo_desktop");
let result = crate::csv_export_directory_from_current_dir(current_dir);
assert_eq!(result, std::path::Path::new("/tmp/khadhroony-bot2/data/exports_csv"));
}
#[test]
fn csv_file_name_accepts_split_entity_exports() {
let mint_result = crate::validated_csv_file_name("replay_mints.csv");
let owner_result = crate::validated_csv_file_name("replay_owners.csv");
let account_result = crate::validated_csv_file_name("replay_account_keys.csv");
assert!(mint_result.is_ok());
assert!(owner_result.is_ok());
assert!(account_result.is_ok());
}
#[test]
fn program_row_exposes_registered_code() {
let row = kb_store::PostgresReplayProgramSummary {
program_id: kb_program_ids::SYSTEM_PROGRAM_ID.to_owned(),
transaction_count: 1,
outer_instruction_count: 1,
inner_instruction_count: 0,
log_count: 1,
min_slot: 1,
max_slot: 1,
};
let converted = crate::program_row_from_pg(row);
assert_eq!(converted.program_code.as_deref(), std::option::Option::Some("system"));
}
}

View File

@@ -266,7 +266,11 @@ pub(crate) async fn demo_ws_connect_inner(
std::result::Result::Ok(request) => request,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let selected_client = match state.ws_pool().select_client_for_role_and_method(&role, &method) {
let pool = match state.demo_ws_pool() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let selected_client = match pool.select_client_for_role_and_method(&role, &method) {
std::result::Result::Ok(client) => client,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};

View File

@@ -12,6 +12,11 @@ mod constants;
mod demo_backfill;
mod demo_http;
mod demo_ws;
mod demo_sql_common;
mod demo_sql_diag;
mod demo_sql_pg_core;
mod demo_sql_pg_raw;
mod demo_sql_replay_candidates;
mod frontend_log;
mod main_window;
mod splash;
@@ -90,6 +95,60 @@ pub(crate) use self::demo_ws::demo_ws_status_inner;
pub(crate) use self::demo_ws::demo_ws_unsubscribe_inner;
/// Disconnects the application WebSocket session during shutdown.
pub(crate) use self::demo_ws::disconnect_demo_ws_app_state;
/// UI-safe SQL table diagnostic snapshot.
pub(crate) use self::demo_sql_common::DemoSqlTableSnapshot;
/// Connects to the configured PostgreSQL store.
pub(crate) use self::demo_sql_common::connect_postgres_store;
/// Formats diagnostic statuses.
pub(crate) use self::demo_sql_common::debug_status;
/// Opens or focuses one SQL demo window.
pub(crate) use self::demo_sql_common::open_sql_demo_window;
/// Builds PostgreSQL options from the active profile.
pub(crate) use self::demo_sql_common::postgres_store_options_from_profile;
/// Converts one PostgreSQL table diagnostic.
pub(crate) use self::demo_sql_common::table_snapshot_from_pg;
/// Converts PostgreSQL table diagnostics.
pub(crate) use self::demo_sql_common::table_snapshots_from_pg;
/// SQL diagnostic payload.
pub(crate) use self::demo_sql_diag::DemoSqlDiagPayload;
/// PostgreSQL core-table payload.
pub(crate) use self::demo_sql_pg_core::DemoSqlPgCorePayload;
/// PostgreSQL raw-table payload.
pub(crate) use self::demo_sql_pg_raw::DemoSqlPgRawPayload;
/// SQL replay entity request.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayEntityRequest;
/// SQL replay entity row.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayEntityRow;
/// Known program option.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayKnownProgramOption;
/// SQL replay options payload.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayOptionsPayload;
/// SQL replay program request.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayProgramRequest;
/// SQL replay program row.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayProgramRow;
/// SQL replay transaction request.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayTransactionRequest;
/// SQL replay transaction row.
pub(crate) use self::demo_sql_replay_candidates::DemoSqlReplayTransactionRow;
/// Resolves an available CSV path.
pub(crate) use self::demo_sql_replay_candidates::available_csv_export_path;
/// Resolves the CSV export directory.
pub(crate) use self::demo_sql_replay_candidates::csv_export_directory_from_current_dir;
/// Parses an entity-kind code.
pub(crate) use self::demo_sql_replay_candidates::entity_kind_from_code;
/// Converts a PostgreSQL entity row.
pub(crate) use self::demo_sql_replay_candidates::entity_row_from_pg;
/// Parses an optional entity-kind code.
pub(crate) use self::demo_sql_replay_candidates::optional_entity_kind_from_code;
/// Converts a PostgreSQL program row.
pub(crate) use self::demo_sql_replay_candidates::program_row_from_pg;
/// Parses a program-scope code.
pub(crate) use self::demo_sql_replay_candidates::program_scope_from_code;
/// Converts a PostgreSQL transaction row.
pub(crate) use self::demo_sql_replay_candidates::transaction_row_from_pg;
/// Validates a CSV filename.
pub(crate) use self::demo_sql_replay_candidates::validated_csv_file_name;
/// Frontend logging payload.
pub(crate) use self::frontend_log::FrontendLogPayload;
/// Emits one normalized frontend log event.

View File

@@ -1,5 +1,5 @@
// file: kb-app-demo-desktop/src/tauri.rs
// version: 6
// version: 7
//! Tauri runtime assembly and private command wrappers.
@@ -52,8 +52,33 @@ pub fn run() -> kb_core::Result<()> {
demo_ws_connect,
demo_ws_unsubscribe,
demo_ws_disconnect,
open_demo_sql_diag_window,
load_demo_sql_diag,
open_demo_sql_pg_raw_window,
load_demo_sql_pg_raw,
open_demo_sql_pg_core_window,
load_demo_sql_pg_core,
open_demo_sql_replay_candidates_window,
demo_sql_replay_options,
load_demo_sql_replay_transactions,
load_demo_sql_replay_programs,
load_demo_sql_replay_entities,
export_demo_sql_replay_csv,
]);
builder = builder.plugin(tracing_builder.build::<tauri::Wry>());
builder = builder.on_window_event(|window, event| {
if window.label() != "demo_ws" {
return;
}
if !matches!(event, tauri::WindowEvent::Destroyed) {
return;
}
let app_handle = window.app_handle().clone();
tauri::async_runtime::spawn(async move {
let state = app_handle.state::<crate::AppState>();
crate::disconnect_demo_ws_app_state(&state, false).await;
});
});
builder = builder.setup(|app| {
let splash_window = match app.get_webview_window("splash") {
std::option::Option::Some(window) => window,
@@ -96,13 +121,6 @@ pub fn run() -> kb_core::Result<()> {
std::option::Option::None,
std::option::Option::None,
);
crate::emit_splash_order(
&splash_window,
"add_log",
std::option::Option::Some("Pool WebSocket initialisé"),
std::option::Option::None,
std::option::Option::None,
);
crate::emit_splash_order(
&splash_window,
"fadein",
@@ -434,7 +452,11 @@ fn open_demo_ws_window(
fn demo_ws_list_pool_clients(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<std::vec::Vec<kb_onchain_transport::WsPoolClientSnapshot>, std::string::String> {
return std::result::Result::Ok(state.ws_pool().snapshot());
let pool = match state.demo_ws_pool() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(pool.snapshot());
}
/// Lists selectable WebSocket roles and methods for the demo UI.
@@ -443,7 +465,10 @@ fn demo_ws_options(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoWsOptionsPayload, std::string::String> {
return std::result::Result::Ok(crate::DemoWsOptionsPayload {
roles: crate::build_ws_role_options(state.ws_pool().snapshot()),
roles: crate::build_ws_role_options(match state.demo_ws_pool() {
std::result::Result::Ok(pool) => pool.snapshot(),
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
}),
methods: crate::build_ws_method_options(),
});
}
@@ -488,3 +513,338 @@ async fn demo_ws_disconnect(
) -> std::result::Result<crate::DemoWsStatusPayload, std::string::String> {
return crate::demo_ws_disconnect_inner(state.inner()).await;
}
/// Opens or focuses the SQL diagnostic demo window.
#[tauri::command]
fn open_demo_sql_diag_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
app_handle,
"demo_sql_diag",
"demo_sql_diag.html",
"Khadhroony Bot3 - SQL diagnostics",
);
}
/// Loads read-only SQL diagnostics from the active profile.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_diag(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlDiagPayload, std::string::String> {
let profile = state.active_profile().clone();
let store_result = crate::connect_postgres_store(&profile).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let diagnostics_result = store.backend_diagnostics().await;
let diagnostics = match diagnostics_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let tables_result = store.known_table_diagnostics().await;
let tables = match tables_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoSqlDiagPayload {
config_path: state.config_path().to_string(),
active_profile_name: profile.name.clone(),
backend: profile.database.backend.clone(),
masked_dsn: store.options().masked_dsn(),
current_schema: diagnostics.descriptor.current_schema,
health_status: crate::debug_status(diagnostics.health.status),
health_message: diagnostics.health.message,
migration_status: crate::debug_status(diagnostics.migrations.status),
migration_message: diagnostics.migrations.message,
server_version: diagnostics.server_version,
tables: crate::table_snapshots_from_pg(tables.as_slice()),
});
}
/// Opens or focuses the PostgreSQL canonical acquisition demo window.
#[tauri::command]
fn open_demo_sql_pg_raw_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
app_handle,
"demo_sql_pg_raw",
"demo_sql_pg_raw.html",
"Khadhroony Bot3 - PostgreSQL canonical acquisition",
);
}
/// Loads read-only raw table diagnostics from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_pg_raw(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlPgRawPayload, std::string::String> {
let profile = state.active_profile().clone();
let store_result = crate::connect_postgres_store(&profile).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let tables_result = store.raw_table_diagnostics().await;
let tables = match tables_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoSqlPgRawPayload {
active_profile_name: profile.name.clone(),
masked_dsn: store.options().masked_dsn(),
tables: crate::table_snapshots_from_pg(tables.as_slice()),
});
}
/// Opens or focuses the PostgreSQL core store demo window.
#[tauri::command]
fn open_demo_sql_pg_core_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
app_handle,
"demo_sql_pg_core",
"demo_sql_pg_core.html",
"Khadhroony Bot3 - PostgreSQL core store",
);
}
/// Loads read-only core table diagnostics from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_pg_core(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlPgCorePayload, std::string::String> {
let profile = state.active_profile().clone();
let store_result = crate::connect_postgres_store(&profile).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let tables_result = store.core_table_diagnostics().await;
let tables = match tables_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
return std::result::Result::Ok(crate::DemoSqlPgCorePayload {
active_profile_name: profile.name.clone(),
masked_dsn: store.options().masked_dsn(),
tables: crate::table_snapshots_from_pg(tables.as_slice()),
});
}
/// Opens or focuses the SQL replay candidate browser.
#[tauri::command]
fn open_demo_sql_replay_candidates_window(
app_handle: tauri::AppHandle,
) -> std::result::Result<(), std::string::String> {
return crate::open_sql_demo_window(
app_handle,
"demo_sql_replay_candidates",
"demo_sql_replay_candidates.html",
"Khadhroony Bot3 - SQL replay candidates",
);
}
/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn export_demo_sql_replay_csv(
file_name: std::string::String,
content: std::string::String,
) -> std::result::Result<std::string::String, std::string::String> {
if content.is_empty() {
return std::result::Result::Err("CSV export content must not be empty".to_owned());
}
if content.len() > 16 * 1024 * 1024 {
return std::result::Result::Err("CSV export content exceeds 16 MiB".to_owned());
}
let normalized_name_result = crate::validated_csv_file_name(&file_name);
let normalized_name = match normalized_name_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let current_dir_result = std::env::current_dir();
let current_dir = match current_dir_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(format!("cannot resolve current directory: {error}"));
},
};
let export_dir = crate::csv_export_directory_from_current_dir(&current_dir);
let create_result = tokio::fs::create_dir_all(&export_dir).await;
if let std::result::Result::Err(error) = create_result {
return std::result::Result::Err(format!("cannot create CSV export directory: {error}"));
}
let export_path_result = crate::available_csv_export_path(&export_dir, normalized_name).await;
let export_path = match export_path_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut bytes = std::vec::Vec::with_capacity(content.len() + 3);
bytes.extend_from_slice(&[0xEF, 0xBB, 0xBF]);
bytes.extend_from_slice(content.as_bytes());
let write_result = tokio::fs::write(&export_path, bytes).await;
if let std::result::Result::Err(error) = write_result {
return std::result::Result::Err(format!("cannot write CSV export: {error}"));
}
let display_path = export_path.to_string_lossy().into_owned();
tracing::info!(target: crate::TRACING_TARGET, path = %display_path, "CSV replay candidate export written");
return std::result::Result::Ok(display_path);
}
/// Loads static browser options from the active PostgreSQL profile.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn demo_sql_replay_options(
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::DemoSqlReplayOptionsPayload, std::string::String> {
let profile = state.active_profile().clone();
let options_result = crate::postgres_store_options_from_profile(&profile, false);
let options = match options_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let mut known_programs =
std::vec::Vec::with_capacity(kb_program_ids::registered_program_ids().len());
for entry in kb_program_ids::registered_program_ids() {
known_programs.push(crate::DemoSqlReplayKnownProgramOption {
code: entry.code().to_owned(),
program_id: entry.program_id().to_owned(),
});
}
return std::result::Result::Ok(crate::DemoSqlReplayOptionsPayload {
active_profile_name: profile.name,
masked_dsn: options.masked_dsn(),
maximum_limit: kb_store::MAX_REPLAY_CANDIDATE_ROWS,
known_programs,
});
}
/// Loads bounded transaction replay candidates from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_replay_transactions(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayTransactionRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayTransactionRow>, std::string::String> {
let program_scope_result = crate::program_scope_from_code(request.program_scope.as_str());
let program_scope = match program_scope_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let entity_kind_result = crate::optional_entity_kind_from_code(request.entity_kind.as_deref());
let entity_kind = match entity_kind_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let filter_result = kb_store::PostgresReplayTransactionFilter::new(
request.signature_contains,
request.min_slot,
request.max_slot,
request.raw_processing_state,
request.ledger_status,
request.program_id,
program_scope,
entity_kind,
request.entity_value,
request.limit,
request.newest_first,
);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result = store.replay_transaction_candidates(&filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay transaction candidates");
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
output.push(crate::transaction_row_from_pg(row));
}
return std::result::Result::Ok(output);
}
/// Loads bounded program summaries from PostgreSQL.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_replay_programs(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayProgramRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayProgramRow>, std::string::String> {
let filter_result =
kb_store::PostgresReplayProgramFilter::new(request.program_id_contains, request.limit);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result = store.replay_program_summaries(&filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay program summaries");
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
output.push(crate::program_row_from_pg(row));
}
return std::result::Result::Ok(output);
}
/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables.
#[tauri::command]
#[allow(clippy::question_mark_used)]
async fn load_demo_sql_replay_entities(
state: tauri::State<'_, crate::AppState>,
request: crate::DemoSqlReplayEntityRequest,
) -> std::result::Result<std::vec::Vec<crate::DemoSqlReplayEntityRow>, std::string::String> {
let entity_kind_result = crate::entity_kind_from_code(request.entity_kind.as_str());
let entity_kind = match entity_kind_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let filter_result = kb_store::PostgresReplayEntityFilter::new(
entity_kind,
request.entity_value_contains,
request.limit,
);
let filter = match filter_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
let store_result = crate::connect_postgres_store(state.active_profile()).await;
let store = match store_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let rows_result = store.replay_entity_summaries(&filter).await;
let rows = match rows_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()),
};
tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay entity summaries");
let mut output = std::vec::Vec::with_capacity(rows.len());
for row in rows {
output.push(crate::entity_row_from_pg(row));
}
return std::result::Result::Ok(output);
}