Files
khadhroony-solana-project/crates/ksp-app-wallet-desk/src/tauri.rs
2026-08-22 10:56:26 +02:00

471 lines
20 KiB
Rust

// 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<tauri::Wry>) -> 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<tauri::Wry>, app_state: crate::AppState) -> tauri::Builder<tauri::Wry> {
return builder.manage(app_state);
}
fn configure_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
let tracing_plugin = tauri_plugin_tracing::Builder::new().build::<tauri::Wry>();
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::Wry>) -> tauri::Builder<tauri::Wry> {
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::Wry>) -> tauri::Builder<tauri::Wry> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletSessionDto, crate::CommandErrorDto> {
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<crate::WalletViewSecurityStatusDto, crate::CommandErrorDto> {
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<std::option::Option<crate::WalletExportResultDto>, 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<crate::RuntimeStatusDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<std::option::Option<crate::WalletTransferInspectionDto>, 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<crate::WalletViewSecurityStatusDto, crate::CommandErrorDto> {
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<crate::WalletBalanceDto, crate::CommandErrorDto> {
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<std::vec::Vec<crate::WalletInventoryEntryDto>, 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<crate::LockedWalletDto, crate::CommandErrorDto> {
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<std::vec::Vec<crate::WalletInventoryEntryDto>, 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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::LockedWalletDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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<crate::WalletAuthorizedDto, crate::CommandErrorDto> {
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)),
};
}