// file: crates/ksp-app-wallet-desk/src/tauri.rs // version: 11 //! Tauri runtime assembly for the KSP wallet desktop application. use tauri_plugin_dialog::DialogExt; // rust-rules: trait-import /// Runs the Wallet Desk 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 = configure_state(builder, app_state); builder = configure_plugins(builder); builder = configure_commands(builder); builder = configure_setup(builder); 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 Wallet 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), ), }; } fn configure_state(builder: tauri::Builder, app_state: crate::AppState) -> tauri::Builder { return builder.manage(app_state); } fn configure_plugins(builder: tauri::Builder) -> tauri::Builder { let tracing_plugin = tauri_plugin_tracing::Builder::new().build::(); return builder.plugin(tauri_plugin_dialog::init()).plugin(tracing_plugin); } #[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![ add_wallet_note, clear_import_source, create_wallet, delete_wallet_note, deselect_wallet, disable_wallet_view, emit_frontend_log, export_wallet_transfer, get_runtime_status, import_wallet, inspect_import_source, list_wallets, lock_wallet, recreate_wallet_view, refresh_wallet_balance, refresh_wallets, rotate_owner_password, rotate_view_password, select_wallet, splash_frontend_ready, unlock_wallet_owner_configured, unlock_wallet_owner_manual, unlock_wallet_view_configured, unlock_wallet_view_manual, update_wallet_alias, update_wallet_note, ]); } fn configure_setup(builder: tauri::Builder) -> tauri::Builder { return 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, "Wallet Desk Tauri shell setup completed"); return std::result::Result::Ok(()); }); } #[tauri::command] async fn add_wallet_note( request: crate::WalletNoteAddRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.add_wallet_note(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] fn clear_import_source(state: tauri::State<'_, crate::AppState>) -> std::result::Result<(), crate::CommandErrorDto> { let result = state.clear_import_source(); return match result { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn create_wallet( request: crate::WalletCreateRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.create_wallet(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn delete_wallet_note( request: crate::WalletNoteDeleteRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.delete_wallet_note(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] fn deselect_wallet(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.deselect_wallet(); return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn disable_wallet_view(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.disable_wallet_view().await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(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(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn export_wallet_transfer( app: tauri::AppHandle, request: crate::WalletExportRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result, crate::CommandErrorDto> { let format = request.format; let default_filename = state.wallet_export_picker_filename(format); let default_filename = match default_filename { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; let extensions = [format.default_extension()]; let selected = app .dialog() .file() .set_title("Export Solana keypair — secret material") .set_file_name(default_filename) .add_filter("Solana keypair transfer", &extensions) .blocking_save_file(); let selected = match selected { std::option::Option::Some(value) => value, std::option::Option::None => { ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_EXPORT, transfer_format = format.code(), "Wallet OWNER transfer export native save picker cancelled"); return std::result::Result::Ok(std::option::Option::None); }, }; let path = selected.into_path(); let path = match path { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => { let error = ksp_core_lib::Error::new( crate::ERROR_CODE_WALLET_EXPORT_DESTINATION_INVALID, "Selected Wallet transfer export destination is not a local filesystem path", ); return std::result::Result::Err(crate::CommandErrorDto::from_error(&error)); }, }; let result = state.export_wallet_transfer(path, request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] fn get_runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.runtime_status(); return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn import_wallet( request: crate::WalletImportRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.import_wallet(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn inspect_import_source( app: tauri::AppHandle, request: crate::WalletImportSourceRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result, crate::CommandErrorDto> { let cleared = state.clear_import_source(); if let std::result::Result::Err(error) = cleared { return std::result::Result::Err(crate::CommandErrorDto::from_error(&error)); } let format = request.format; let extensions = [format.default_extension()]; let selected = app .dialog() .file() .set_title("Select Solana keypair transfer source") .add_filter("Solana keypair transfer", &extensions) .blocking_pick_file(); let selected = match selected { std::option::Option::Some(value) => value, std::option::Option::None => { ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_IMPORT, transfer_format = format.code(), "Wallet import native picker cancelled"); return std::result::Result::Ok(std::option::Option::None); }, }; let path = selected.into_path(); let path = match path { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => { let error = ksp_core_lib::Error::new(crate::ERROR_CODE_WALLET_IMPORT_SOURCE_INVALID, "Selected Wallet transfer source is not a local filesystem path"); return std::result::Result::Err(crate::CommandErrorDto::from_error(&error)); }, }; let result = state.stage_import_source(path, format).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn recreate_wallet_view( request: crate::WalletPasswordRotationRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.recreate_wallet_view(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn refresh_wallet_balance(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.refresh_wallet_balance().await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn list_wallets(state: tauri::State<'_, crate::AppState>) -> std::result::Result, crate::CommandErrorDto> { let root = state.wallet_inventory_root().to_path_buf(); let result = crate::list_wallet_inventory(root.as_path()).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn lock_wallet(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.lock_wallet().await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn refresh_wallets( state: tauri::State<'_, crate::AppState>, ) -> std::result::Result, crate::CommandErrorDto> { let deselected = state.deselect_wallet(); if let std::result::Result::Err(error) = deselected { return std::result::Result::Err(crate::CommandErrorDto::from_error(&error)); } let root = state.wallet_inventory_root().to_path_buf(); let result = crate::list_wallet_inventory(root.as_path()).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn rotate_owner_password( request: crate::WalletPasswordRotationRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.rotate_owner_password(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn rotate_view_password( request: crate::WalletPasswordRotationRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.rotate_view_password(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn select_wallet( request: crate::WalletSelectionRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.select_wallet(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn splash_frontend_ready( app: tauri::AppHandle, window: tauri::WebviewWindow, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result<(), crate::CommandErrorDto> { let result = crate::splash_frontend_ready_service(app, window, &state).await; return match result { std::result::Result::Ok(()) => std::result::Result::Ok(()), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn unlock_wallet_owner_configured(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.unlock_wallet_owner_configured().await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn unlock_wallet_owner_manual( request: crate::WalletUnlockRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.unlock_wallet_owner_manual(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn unlock_wallet_view_configured(state: tauri::State<'_, crate::AppState>) -> std::result::Result { let result = state.unlock_wallet_view_configured().await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn unlock_wallet_view_manual( request: crate::WalletUnlockRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.unlock_wallet_view_manual(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn update_wallet_alias( request: crate::WalletAliasUpdateRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.update_wallet_alias(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; } #[tauri::command] async fn update_wallet_note( request: crate::WalletNoteUpdateRequestDto, state: tauri::State<'_, crate::AppState>, ) -> std::result::Result { let result = state.update_wallet_note(request).await; return match result { std::result::Result::Ok(value) => std::result::Result::Ok(value), std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)), }; }