Skip to content

Commit

Permalink
Refactor: error handling (#68)
Browse files Browse the repository at this point in the history
* chore(back): update deps;

* refactor(back): error handling. remove custom result object;

* refactor(front): error handling;

* fix(front): modal window z-index;

* feat(front): add folder light icon;

* refactor: small refactoring;
  • Loading branch information
TheBestTvarynka authored Jan 3, 2025
1 parent 3f2b97e commit cae35db
Show file tree
Hide file tree
Showing 25 changed files with 894 additions and 667 deletions.
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

0 comments on commit cae35db

Please sign in to comment.