mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
refactor(gui-client): improve error handling of Tauri commands (#9380)
Introducing a dedicated `Error` type allows us to directly serialise `anyhow::Error`s to the GUI. Those will then be reported to Sentry on the TS side.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
use std::{path::PathBuf, time::Duration};
|
||||
|
||||
use anyhow::{Context as _, Result, bail};
|
||||
use anyhow::Context as _;
|
||||
use firezone_logging::err_with_src;
|
||||
use serde::Serialize;
|
||||
use tauri::{Wry, ipc::Invoke};
|
||||
use tauri_plugin_dialog::DialogExt as _;
|
||||
|
||||
@@ -24,39 +25,34 @@ pub fn generate_handler() -> impl Fn(Invoke<Wry>) -> bool + Send + Sync + 'stati
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn clear_logs(managed: tauri::State<'_, Managed>) -> Result<(), String> {
|
||||
async fn clear_logs(managed: tauri::State<'_, Managed>) -> Result<()> {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
if let Err(error) = managed.ctlr_tx.send(ControllerRequest::ClearLogs(tx)).await {
|
||||
// Tauri will only log errors to the JS console for us, so log this ourselves.
|
||||
tracing::error!(
|
||||
"Error while asking `Controller` to clear logs: {}",
|
||||
err_with_src(&error)
|
||||
);
|
||||
return Err(error.to_string());
|
||||
}
|
||||
if let Err(error) = rx.await {
|
||||
tracing::error!(
|
||||
"Error while awaiting log-clearing operation: {}",
|
||||
err_with_src(&error)
|
||||
);
|
||||
return Err(error.to_string());
|
||||
}
|
||||
|
||||
managed
|
||||
.ctlr_tx
|
||||
.send(ControllerRequest::ClearLogs(tx))
|
||||
.await
|
||||
.context("Failed to send `ClearLogs` command")?;
|
||||
|
||||
rx.await
|
||||
.context("Failed to await `ClearLogs` result")?
|
||||
.map_err(anyhow::Error::msg)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn export_logs(
|
||||
app: tauri::AppHandle,
|
||||
managed: tauri::State<'_, Managed>,
|
||||
) -> Result<(), String> {
|
||||
show_export_dialog(&app, managed.ctlr_tx.clone()).map_err(|e| e.to_string())
|
||||
async fn export_logs(app: tauri::AppHandle, managed: tauri::State<'_, Managed>) -> Result<()> {
|
||||
show_export_dialog(&app, managed.ctlr_tx.clone())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn apply_advanced_settings(
|
||||
managed: tauri::State<'_, Managed>,
|
||||
settings: AdvancedSettings,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<()> {
|
||||
if managed.inner().inject_faults {
|
||||
tokio::time::sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
@@ -65,13 +61,13 @@ async fn apply_advanced_settings(
|
||||
.ctlr_tx
|
||||
.send(ControllerRequest::ApplySettings(Box::new(settings)))
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
.context("Failed to send `ApplySettings` command")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn reset_advanced_settings(managed: tauri::State<'_, Managed>) -> Result<(), String> {
|
||||
async fn reset_advanced_settings(managed: tauri::State<'_, Managed>) -> Result<()> {
|
||||
apply_advanced_settings(managed, AdvancedSettings::default()).await?;
|
||||
|
||||
Ok(())
|
||||
@@ -83,9 +79,9 @@ fn show_export_dialog(app: &tauri::AppHandle, ctlr_tx: CtlrTx) -> Result<()> {
|
||||
let datetime_string = now.format("%Y_%m_%d-%H-%M");
|
||||
let stem = PathBuf::from(format!("firezone_logs_{datetime_string}"));
|
||||
let filename = stem.with_extension("zip");
|
||||
let Some(filename) = filename.to_str() else {
|
||||
bail!("zip filename isn't valid Unicode");
|
||||
};
|
||||
let filename = filename
|
||||
.to_str()
|
||||
.context("zip filename isn't valid Unicode")?;
|
||||
|
||||
tauri_plugin_dialog::FileDialogBuilder::new(app.dialog().clone())
|
||||
.add_filter("Zip", &["zip"])
|
||||
@@ -112,37 +108,54 @@ fn show_export_dialog(app: &tauri::AppHandle, ctlr_tx: CtlrTx) -> Result<()> {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn sign_in(managed: tauri::State<'_, Managed>) -> Result<(), String> {
|
||||
async fn sign_in(managed: tauri::State<'_, Managed>) -> Result<()> {
|
||||
managed
|
||||
.ctlr_tx
|
||||
.send(ControllerRequest::SignIn)
|
||||
.await
|
||||
.context("Failed to send `SignIn` command")
|
||||
.map_err(|e| e.to_string())?;
|
||||
.context("Failed to send `SignIn` command")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn sign_out(managed: tauri::State<'_, Managed>) -> Result<(), String> {
|
||||
async fn sign_out(managed: tauri::State<'_, Managed>) -> Result<()> {
|
||||
managed
|
||||
.ctlr_tx
|
||||
.send(ControllerRequest::SignOut)
|
||||
.await
|
||||
.context("Failed to send `SignOut` command")
|
||||
.map_err(|e| e.to_string())?;
|
||||
.context("Failed to send `SignOut` command")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn update_state(managed: tauri::State<'_, Managed>) -> Result<(), String> {
|
||||
async fn update_state(managed: tauri::State<'_, Managed>) -> Result<()> {
|
||||
managed
|
||||
.ctlr_tx
|
||||
.send(ControllerRequest::UpdateState)
|
||||
.await
|
||||
.context("Failed to send `UpdateState` command")
|
||||
.map_err(|e| e.to_string())?;
|
||||
.context("Failed to send `UpdateState` command")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Error(anyhow::Error);
|
||||
|
||||
impl Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&format!("{:#}", self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<anyhow::Error> for Error {
|
||||
fn from(value: anyhow::Error) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user