Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: error handling #68

Merged
merged 6 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,110 changes: 689 additions & 421 deletions dataans/Cargo.lock

Large diffs are not rendered by default.

74 changes: 16 additions & 58 deletions dataans/common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,27 @@ use std::fmt;

use serde::{Deserialize, Serialize};

/// Custom result type.
/// Error object returned from the Tauri command.
///
/// The frontend does not need to know details of the error. A simple [String] is enough.
/// If the user wants more info about the error, they can find it in the dataans log file.
/// [CommandError] is shared between app frontend and backend.
#[derive(Debug, Serialize, Deserialize)]
pub struct DataansResult<T> {
ok: Option<T>,
err: Option<String>,
pub enum CommandError {
/// Any error inside app backend.
Dataans(String),
/// Error during deserialization from [JsValue] or serialization into [JsValue].
JsValue(String),
}

impl<T> DataansResult<T> {
/// Create ok.
pub fn ok(ok: T) -> Self {
Self {
ok: Some(ok),
err: None,
}
}

/// Create err.
pub fn err(err: String) -> Self {
Self {
ok: None,
err: Some(err),
}
}

/// Check result status.
pub fn is_ok(&self) -> bool {
self.ok.is_some()
}
}

impl<T> From<DataansResult<T>> for Result<T, String> {
fn from(result: DataansResult<T>) -> Result<T, String> {
let DataansResult { ok, err } = result;

if let Some(ok) = ok {
Ok(ok)
} else {
Err(err.expect("Err obj should present"))
}
}
}

impl<T, E: fmt::Display> From<Result<T, E>> for DataansResult<T> {
fn from(err: Result<T, E>) -> Self {
match err {
Ok(value) => DataansResult::ok(value),
Err(err) => DataansResult::err(err.to_string()),
}
impl fmt::Display for CommandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}

/// Dummy unit type because `()` is not enough :).
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct DummyUnit(u8);
/// Result type of the Tauri command.
pub type CommandResult<T> = Result<T, CommandError>;

impl<E: fmt::Display> From<Result<(), E>> for DataansResult<DummyUnit> {
fn from(err: Result<(), E>) -> Self {
match err {
Ok(_) => DataansResult::ok(DummyUnit::default()),
Err(err) => DataansResult::err(err.to_string()),
}
}
}
/// Empty Tauri command result.
///
/// Use this type when the Tauri command should not return any data but may fail.
pub type CommandResultEmpty = Result<(), CommandError>;
2 changes: 1 addition & 1 deletion dataans/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]

/// Contains general error type for tauri frontend and backend.
/// Contains general error and result types for Tauri commands.
pub mod error;
/// Contains schema definitions for data export.
pub mod export;
Expand Down
1 change: 1 addition & 0 deletions dataans/public/css/modal.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
justify-content: center;
align-items: center;
background-color: rgba(0.3, 0.3, 0.3, 0.7);
z-index: 2;
}
File renamed without changes
Binary file added dataans/public/icons/folder-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions dataans/src-tauri/src/dataans/command/export/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ mod md;
use std::fs;
use std::path::{Path, PathBuf};

use common::error::CommandResult;
use common::DataExportConfig;
use tauri::State;
use time::macros::format_description;
use time::OffsetDateTime;

use crate::dataans::command::CommandResult;
use crate::dataans::{DataansError, DataansState};
use crate::BACKUPS_DIR;

Expand Down Expand Up @@ -56,5 +56,5 @@ pub async fn export_app_data(
state: State<'_, DataansState>,
export_config: DataExportConfig,
) -> CommandResult<PathBuf> {
Ok(export_data(state, export_config).await.into())
Ok(export_data(state, export_config).await?)
}
15 changes: 5 additions & 10 deletions dataans/src-tauri/src/dataans/command/file.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use common::error::{CommandResult, CommandResultEmpty};
use common::note::File;
use tauri::State;
use uuid::Uuid;

use super::{CommandResult, CommandResultEmpty};
use crate::dataans::DataansState;

#[instrument(ret, skip(state))]
Expand All @@ -11,28 +11,23 @@ pub async fn upload_file(state: State<'_, DataansState>, id: Uuid, name: String,
Ok(state
.file_service
.upload_file(id, name, &data, &state.app_data_dir)
.await
.into())
.await?)
}

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn delete_file(state: State<'_, DataansState>, id: Uuid) -> CommandResultEmpty {
Ok(state.file_service.delete_file(id).await.into())
Ok(state.file_service.delete_file(id).await?)
}

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn gen_random_avatar(state: State<'_, DataansState>) -> CommandResult<File> {
Ok(state.file_service.gen_random_avatar(&state.app_data_dir).await.into())
Ok(state.file_service.gen_random_avatar(&state.app_data_dir).await?)
}

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn handle_clipboard_image(state: State<'_, DataansState>) -> CommandResult<File> {
Ok(state
.file_service
.handle_clipboard_image(&state.app_data_dir)
.await
.into())
Ok(state.file_service.handle_clipboard_image(&state.app_data_dir).await?)
}
5 changes: 0 additions & 5 deletions dataans/src-tauri/src/dataans/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,3 @@ pub mod export;
pub mod file;
pub mod note;
pub mod space;

use common::error::{DataansResult, DummyUnit};

type CommandResult<T> = Result<DataansResult<T>, ()>;
type CommandResultEmpty = Result<DataansResult<DummyUnit>, ()>;
14 changes: 7 additions & 7 deletions dataans/src-tauri/src/dataans/command/note.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
use common::error::{CommandResult, CommandResultEmpty};
use common::note::{Id as NoteId, NoteFullOwned, OwnedNote, UpdateNote};
use common::space::Id as SpaceId;
use tauri::State;

use super::{CommandResult, CommandResultEmpty};
use crate::dataans::DataansState;

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn list_notes(state: State<'_, DataansState>, space_id: SpaceId) -> CommandResult<Vec<OwnedNote>> {
Ok(state.note_service.space_notes(space_id).await.into())
Ok(state.note_service.space_notes(space_id).await?)
}

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn create_note(state: State<'_, DataansState>, note: OwnedNote) -> CommandResultEmpty {
Ok(state.note_service.create_note(note).await.into())
Ok(state.note_service.create_note(note).await?)
}

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn update_note(state: State<'_, DataansState>, note_data: UpdateNote<'_>) -> CommandResultEmpty {
Ok(state.note_service.update_note(note_data).await.into())
Ok(state.note_service.update_note(note_data).await?)
}

#[instrument(ret, skip(state))]
#[tauri::command]
pub async fn delete_note(state: State<'_, DataansState>, note_id: NoteId) -> CommandResultEmpty {
Ok(state.note_service.delete_note(note_id).await.into())
Ok(state.note_service.delete_note(note_id).await?)
}

#[instrument(level = "trace", ret, skip(state))]
Expand All @@ -36,11 +36,11 @@ pub async fn search_notes_in_space(
query: String,
space_id: SpaceId,
) -> CommandResult<Vec<NoteFullOwned>> {
Ok(state.note_service.search_notes_in_space(&query, space_id).await.into())
Ok(state.note_service.search_notes_in_space(&query, space_id).await?)
}

#[instrument(level = "trace", ret, skip(state))]
#[tauri::command]
pub async fn search_notes(state: State<'_, DataansState>, query: String) -> CommandResult<Vec<NoteFullOwned>> {
Ok(state.note_service.search_notes(&query).await.into())
Ok(state.note_service.search_notes(&query).await?)
}
10 changes: 5 additions & 5 deletions dataans/src-tauri/src/dataans/command/space.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
use common::error::{CommandResult, CommandResultEmpty};
use common::space::{DeleteSpace, OwnedSpace, UpdateSpace};
use tauri::State;

use super::{CommandResult, CommandResultEmpty};
use crate::dataans::DataansState;

#[instrument(level = "trace", ret, skip(state))]
#[tauri::command]
pub async fn list_spaces(state: State<'_, DataansState>) -> CommandResult<Vec<OwnedSpace>> {
Ok(state.space_service.spaces().await.into())
Ok(state.space_service.spaces().await?)
}

#[instrument(level = "trace", ret, skip(state))]
#[tauri::command]
pub async fn create_space(state: State<'_, DataansState>, space_data: OwnedSpace) -> CommandResultEmpty {
Ok(state.space_service.create_space(space_data).await.into())
Ok(state.space_service.create_space(space_data).await?)
}

#[instrument(level = "trace", ret, skip(state))]
#[tauri::command]
pub async fn update_space(state: State<'_, DataansState>, space_data: UpdateSpace<'static>) -> CommandResultEmpty {
Ok(state.space_service.update_space(space_data).await.into())
Ok(state.space_service.update_space(space_data).await?)
}

#[instrument(level = "trace", ret, skip(state))]
#[tauri::command]
pub async fn delete_space(state: State<'_, DataansState>, space_data: DeleteSpace) -> CommandResultEmpty {
Ok(state.space_service.delete_space(space_data).await.into())
Ok(state.space_service.delete_space(space_data).await?)
}
7 changes: 7 additions & 0 deletions dataans/src-tauri/src/dataans/error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::io::Error as IoError;
use std::path::PathBuf;

use common::error::CommandError;
use thiserror::Error;

use crate::dataans::db::DbError;
Expand Down Expand Up @@ -34,3 +35,9 @@ pub enum DataansError {
#[error("Can not create an image from raw image data")]
ImageFromRaw,
}

impl From<DataansError> for CommandError {
fn from(error: DataansError) -> Self {
Self::Dataans(error.to_string())
}
}
7 changes: 6 additions & 1 deletion dataans/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,12 @@ fn main() {
config::open_theme_file,
code_block::parse_code,
])
.build(tauri::generate_context!())
.build(
{
#![allow(deprecated)]
tauri::generate_context!()
},
)
.expect("error while building tauri application")
.run(|_app_handle, event| {
if let RunEvent::ExitRequested { api, .. } = event {
Expand Down
22 changes: 15 additions & 7 deletions dataans/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,24 @@ pub fn App() -> impl IntoView {

let global_config = expect_context::<RwSignal<Config>>();
spawn_local(async move {
let config = load_config().await;
info!("config: {:?}", config);
match load_config().await {
Ok(config) => {
info!("{:?}", config);

let theme = config.appearance.theme.clone();
let theme = config.appearance.theme.clone();

global_config.set(config.clone());
set_config.set(config);
global_config.set(config.clone());
set_config.set(config);

let theme = load_theme(&theme).await;
set_theme_css.set(theme.to_css());
// TODO.
let theme = load_theme(&theme).await.unwrap();
set_theme_css.set(theme.to_css());
}
Err(err) => {
error!("{:?}", err);
// TODO: toastr.
}
}
});

let global_state = expect_context::<RwSignal<GlobalState>>();
Expand Down
22 changes: 16 additions & 6 deletions dataans/src/app_info/app_info_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,25 @@ pub fn AppInfoWindow(#[prop(into)] close: Callback<(), ()>) -> impl IntoView {

let enable_autostart = move |_| {
spawn_local(async move {
let flag = crate::backend::autostart::enable().await;
set_autostart.set(flag);
match crate::backend::autostart::enable().await {
Ok(flag) => set_autostart.set(flag),
Err(err) => {
error!("{:?}", err);
// TODO: toastr.
}
}
})
};

let disable_autostart = move |_| {
spawn_local(async move {
let flag = crate::backend::autostart::disable().await;
set_autostart.set(flag);
match crate::backend::autostart::disable().await {
Ok(flag) => set_autostart.set(flag),
Err(err) => {
error!("{:?}", err);
// TODO: toastr.
}
}
})
};

Expand All @@ -53,7 +63,7 @@ pub fn AppInfoWindow(#[prop(into)] close: Callback<(), ()>) -> impl IntoView {
title="Open config file location"
on:click=open_config_file_folder
>
<img alt="edit note" src="/public/icons/folder.png" />
<img alt="edit note" src="/public/icons/folder-light.png" />
</button>
{move || if is_autostart_enabled.get() {view! {
<button class="button_ok" on:click=disable_autostart title="Disable autostart">"Disable autostart"</button>
Expand Down Expand Up @@ -112,7 +122,7 @@ pub fn AppInfoWindow(#[prop(into)] close: Callback<(), ()>) -> impl IntoView {
title="Open theme file location"
on:click=move |_| open_theme_file(theme.clone())
>
<img alt="edit note" src="/public/icons/folder.png" />
<img alt="edit note" src="/public/icons/folder-light.png" />
</button>
</td>
</tr>
Expand Down
4 changes: 2 additions & 2 deletions dataans/src/app_info/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn Export() -> impl IntoView {
async move {
match export_data(export_config).await {
Ok(backup_dir) => set_backup_dir.set(Some(backup_dir)),
Err(err) => error!("{}", err),
Err(err) => error!("{:?}", err),
}
}
});
Expand Down Expand Up @@ -102,7 +102,7 @@ pub fn Export() -> impl IntoView {
title="Open backup folder"
on:click=move |_| open_backup_folder(backup_dir.clone())
>
<img alt="edit note" src="/public/icons/folder.png" />
<img alt="edit note" src="/public/icons/folder-light.png" />
</button>
}
}}
Expand Down
Loading
Loading