// file: crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs // version: 9 //! Tauri runtime assembly for the KSP Raw Transaction Ingest desktop application. use tauri::Emitter; // rust-rules: trait-import use tauri::Manager; // rust-rules: trait-import /// Runs the Raw Transaction Ingest desktop application. #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<()> { let context = tauri::generate_context!(); let runtime_layout = configure_packaged_runtime(&context); if let std::result::Result::Err(error) = runtime_layout { return std::result::Result::Err(error); } let app_state = crate::AppState::initialize(arguments); let app_state = match app_state { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let mut builder = tauri::Builder::default(); builder = builder.manage(app_state); builder = builder.plugin(tauri_plugin_tracing::Builder::new().build::()); builder = configure_commands(builder); builder = configure_window_events(builder); builder = builder.setup(|app| { let splash = crate::require_splash_window(app); if let std::result::Result::Err(error) = splash { return std::result::Result::Err(std::boxed::Box::new(error)); } let main = crate::require_main_window(app); if let std::result::Result::Err(error) = main { return std::result::Result::Err(std::boxed::Box::new(error)); } ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_SHELL, "Raw Transaction Ingest Desk Tauri shell setup completed"); return std::result::Result::Ok(()); }); let run_result = builder.run(context); return match run_result { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err( ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot run KSP Raw Transaction Ingest Desk Tauri runtime") .with_context("tauri_error", error.to_string()), ), }; } fn configure_packaged_runtime(context: &tauri::Context) -> ksp_core_lib::Result<()> { if cfg!(debug_assertions) { return std::result::Result::Ok(()); } let resource_root = tauri::utils::platform::resource_dir(context.package_info(), &tauri::Env::default()); let resource_root = match resource_root { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err( ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot resolve packaged Tauri resource directory") .with_context("tauri_error", error.to_string()), ); }, }; let layout = ksp_config_lib::prepare_packaged_runtime(resource_root.as_path()); let layout = match layout { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let working_directory = std::env::set_current_dir(layout.runtime_root()); return match working_directory { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err( ksp_core_lib::Error::new(crate::ERROR_CODE_TAURI_RUNTIME_FAILED, "Cannot activate packaged KSP runtime directory") .with_context("runtime_root", layout.runtime_root().to_string_lossy().into_owned()) .with_source(error), ), }; } #[allow(clippy::question_mark_used)] // Tauri generates the question-mark operator internally for async command dispatch. fn configure_commands(builder: tauri::Builder) -> tauri::Builder { return builder.invoke_handler(tauri::generate_handler![ emit_frontend_log, get_route_foundation, get_route_inventory, get_route_monitoring, get_runtime_status, splash_frontend_ready, start_route, stop_route, validate_route_start ]); } fn configure_window_events(builder: tauri::Builder) -> tauri::Builder { return builder.on_window_event(|window, event| { if window.label() != "main" { return; } if let tauri::WindowEvent::CloseRequested { api, .. } = event { api.prevent_close(); let app_handle = window.app_handle().clone(); let state = app_handle.state::(); let begin = state.begin_shutdown(); let begin = match begin { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { ksp_logging_lib::warn!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WINDOWS, error_domain = error.code().domain(), error_code = error.code().code(), "Raw Transaction Ingest Desk could not enter graceful shutdown; exiting after safe error projection" ); app_handle.exit(1); return; }, }; if !begin { return; } ksp_logging_lib::debug!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WINDOWS, "Raw Transaction Ingest Desk main-window close requested; route Start admission closed and cooperative shutdown requested" ); tauri::async_runtime::spawn(async move { let state = app_handle.state::(); let shutdown = state.shutdown_routes().await; match shutdown { std::result::Result::Ok(()) => { ksp_logging_lib::debug!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WINDOWS, "Raw Transaction Ingest Desk route and shared Store shutdown completed; exiting application" ); app_handle.exit(0); }, std::result::Result::Err(error) => { ksp_logging_lib::warn!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WINDOWS, error_domain = error.code().domain(), error_code = error.code().code(), "Raw Transaction Ingest Desk bounded route shutdown failed; exiting application after bounded cleanup attempt" ); app_handle.exit(1); }, } }); } }); } fn project_command_error(command: &'static str, domain: &'static str, error: &ksp_core_lib::Error) -> crate::CommandErrorDto { ksp_logging_lib::warn!( target: crate::TRACING_TARGET, domain = domain, command = command, error_domain = error.code().domain(), error_code = error.code().code(), "Raw Transaction Ingest Desk Tauri command failed" ); return crate::CommandErrorDto::from_error(error); } #[tauri::command] fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Result<(), crate::CommandErrorDto> { let result = crate::emit_frontend_log_event(payload); return match result { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("emit_frontend_log", crate::TRACING_DOMAIN_FRONTEND, &error)), }; } #[tauri::command] fn get_route_foundation(state: tauri::State<'_, crate::AppState>) -> crate::RawIngestRouteFoundationDto { return state.route_foundation(); } #[tauri::command] fn get_route_inventory(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.route_inventory(); return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("get_route_inventory", crate::TRACING_DOMAIN_INVENTORY, &error)), }; } #[tauri::command] fn get_route_monitoring( state: tauri::State<'_, crate::AppState>, ) -> std::result::Result, crate::CommandErrorDto> { let result = state.route_monitoring(); return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("get_route_monitoring", crate::TRACING_DOMAIN_ROUTE_RUNTIME, &error)), }; } #[tauri::command] fn get_runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.shell_status(); return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("get_runtime_status", crate::TRACING_DOMAIN_SHELL, &error)), }; } #[tauri::command] async fn splash_frontend_ready( app: tauri::AppHandle, webview_window: tauri::WebviewWindow, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result<(), crate::CommandErrorDto> { let result = crate::splash_frontend_ready_service(app, webview_window, &state).await; return match result { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("splash_frontend_ready", crate::TRACING_DOMAIN_WINDOWS, &error)), }; } #[tauri::command] async fn start_route( app: tauri::AppHandle, request: crate::RawIngestRouteStartRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let launch = state.start_route(&request).await; let launch = match launch { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err(project_command_error("start_route", crate::TRACING_DOMAIN_ROUTE_RUNTIME, &error)); }, }; let acknowledgement = launch.acknowledgement(); let monitoring_identity = launch.monitoring_identity(); let monitoring_source = launch.monitoring_source(); let monitoring_app = app.clone(); let monitoring_task = tauri::async_runtime::spawn(async move { monitor_route_status(monitoring_app, monitoring_identity, monitoring_source).await; }); std::mem::drop(monitoring_task); let terminal_task = tauri::async_runtime::spawn(async move { launch.monitor().await; }); std::mem::drop(terminal_task); return std::result::Result::Ok(acknowledgement); } const ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS: u64 = 500; async fn monitor_route_status( app: tauri::AppHandle, runtime: crate::RawIngestRouteRuntimeDto, source: ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSnapshotSource, ) { let mut snapshot = source.current(); loop { let projected = crate::project_route_monitoring(&runtime, &snapshot); match projected { std::result::Result::Ok(status) => { let main = app.get_webview_window("main"); if let std::option::Option::Some(window) = main { let emitted = window.emit(crate::RAW_INGEST_ROUTE_STATUS_EVENT_NAME, status); if emitted.is_err() { ksp_logging_lib::warn!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME, "Raw Transaction Ingest Desk could not emit latest-value route monitoring status" ); } } }, std::result::Result::Err(error) => { ksp_logging_lib::warn!( target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME, error_domain = error.code().domain(), error_code = error.code().code(), "Raw Transaction Ingest Desk could not project latest-value route monitoring status" ); }, } if snapshot.worker_snapshot().state().is_terminal() { return; } let observed = snapshot.worker_snapshot().sequence(); snapshot = source.wait_for_change(observed).await; if !snapshot.worker_snapshot().state().is_terminal() { tokio::time::sleep(std::time::Duration::from_millis(ROUTE_MONITORING_MIN_EMIT_INTERVAL_MS)).await; snapshot = source.current(); } } } #[tauri::command] async fn stop_route( request: crate::RawIngestRouteStopRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.stop_route(&request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("stop_route", crate::TRACING_DOMAIN_ROUTE_RUNTIME, &error)), }; } #[tauri::command] async fn validate_route_start( request: crate::RawIngestRouteStartRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.validate_route_start(&request); return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(project_command_error("validate_route_start", crate::TRACING_DOMAIN_ROUTE_START, &error)), }; }