feat: improved logging and error handling
mellbacon committed
Sep 27, 2023 at 00:28 UTC
c140d82e6f848e0abbfb03b3b935366d07551ffe
6 files changed
+72
-23
src-tauri/src/main.rs
+49
-10
@@ -13,7 +13,7 @@ use std::collections::HashMap;
13
use std::ffi::OsStr;
14
use std::fs::File;
15
use std::io::Write;
16
-use std::path::Path;
16
+use std::path::{Path, PathBuf};
17
use std::{env, fs};
18
use std::process::Command;
19
@@ -23,7 +23,7 @@ mod encoding;
23
use encoding::BOM;
24
use tauri::plugin::TauriPlugin;
25
use tauri_plugin_log::{LogTarget, RotationStrategy};
26
-use log::info;
26
+use log::{info, error};
27
28
use crate::encoding::convert_to_u16;
29
@@ -59,14 +59,27 @@ fn attempt_file_access(app_handle: tauri::AppHandle, p: &str) {
59
fn delete_file(path: &str, perm: bool) {
60
if perm {
61
if is_file(path) {
62
- fs::remove_file(path).unwrap();
62
+ match fs::remove_file(path) {
63
+ Ok(_) => {
64
+ info!("{:?} sucessfully deleted.", PathBuf::from(path).file_name());
65
+ },
66
+ Err(err) => error!("Cannot remove {}. Error: {}.", path, err),
67
+ }
68
}
69
else {
65
- fs::remove_dir_all(path).unwrap();
70
+ match fs::remove_dir_all(path) {
71
+ Ok(_) => info!("Path {} deleted sucessfully.", path),
72
+ Err(err) => error!("Cannot remove {}. Error: {}.", path, err)
73
+ }
74
}
75
}
76
else {
69
- trash::delete(path).unwrap();
77
+ match trash::delete(path) {
78
+ Ok(_) => {
79
+ info!("{:?} sucessfully moved to trash.", PathBuf::from(path).file_name());
80
+ },
81
+ Err(err) => error!("Cannot remove {}. Error: {}.", path, err),
82
+ }
83
}
84
}
85
@@ -81,30 +94,43 @@ struct FileData {
94
95
#[tauri::command]
96
fn read_file(path: &str) -> FileData {
84
- let bytes = fs::read(path).unwrap();
97
+ info!("Attempting to read file in {}.", path);
98
+
99
+ let mut bytes: Vec<u8> = vec![];
100
+ match fs::read(path) {
101
+ Ok(b) => {
102
+ bytes = b;
103
+ },
104
+ Err(err) => error!("Cannot read {}. Error: {}", path, err)
105
+ }
106
let ext = Path::new(path).extension().and_then(OsStr::to_str).unwrap();
107
let file_data: FileData;
108
109
// encode based on bom if present otherwise just default to utf8
110
if let Some(data) = encoding_rs::Encoding::for_bom(&bytes) {
111
let (text, encoding, _) = data.0.decode(&bytes);
91
- file_data = FileData {text: text.to_string(), encoding: encoding.name().to_string(), extension: ext.to_string(), bom: true}
112
+ file_data = FileData {text: text.to_string(), encoding: encoding.name().to_string(), extension: ext.to_string(), bom: true};
113
+ info!("File BOM found. Encoding with {}...", encoding.name());
114
}
115
else {
116
let (text, encoding, _) = encoding_rs::UTF_8.decode(&bytes);
95
- file_data = FileData {text: text.to_string(), encoding: encoding.name().to_string(), extension: ext.to_string(), bom: false}
117
+ file_data = FileData {text: text.to_string(), encoding: encoding.name().to_string(), extension: ext.to_string(), bom: false};
118
+ info!("No file BOM found. Defaulting to {} encoding...", encoding.name());
119
}
120
file_data
121
}
122
123
#[tauri::command]
124
fn write_file(path: &str, content: &str, enc: &str, has_bom: bool) {
125
+ info!("Attempting to write file to {}.", path);
126
+
127
let mut output = Vec::new();
128
if let Some(data) = encoding_rs::Encoding::for_label(enc.as_bytes()) {
129
let (bytes, _, _) = data.encode(content);
130
let mut c_bytes = bytes.to_vec();
131
let mut bom: Vec<u8> = Vec::new();
132
if has_bom {
133
+ info!("Encoding file to {} encoding...", enc);
134
if enc == "UTF-8" {
135
bom = b"\xEF\xBB\xBF".to_vec();
136
}
@@ -117,8 +143,18 @@ fn write_file(path: &str, content: &str, enc: &str, has_bom: bool) {
143
}
144
output = [bom.as_slice(), c_bytes.as_slice()].concat();
145
}
120
- let mut file = File::create(path).unwrap();
121
- file.write_all(&output).unwrap();
146
+
147
+ if !PathBuf::from(path).exists() {
148
+ info!("{} not found. Creating new file...", path);
149
+ }
150
+
151
+ let file = File::create(path);
152
+ match file {
153
+ Ok(mut f) => {
154
+ f.write_all(&output).unwrap();
155
+ },
156
+ Err(err) => error!("Cannot write to {}. Error: {}", path, err)
157
+ }
158
}
159
160
fn configure_log() -> TauriPlugin<Wry> {
@@ -182,6 +218,9 @@ fn load_settings(app: &mut App) {
218
fs::write(&settings_path, default_settings.to_string()).unwrap();
219
info!("Default settings file not found. Created a new default settings file. Path: {:?}", &settings_path);
220
}
221
+ else {
222
+ info!("Settings path: {:?}:", settings_path);
223
+ }
224
225
let mut defaults = HashMap::new();
226
for settings in default_settings.as_object().unwrap() {
src/config/commands.ts
+2
-2
@@ -190,7 +190,7 @@ export const commands = {
190
"keybind": "F2",
191
"command": async (filename, oldpath) => {
192
if (!await fs.exists(oldpath)) {
193
- warn(`${oldpath} does not exist!`);
193
+ warn(`Unable to rename file. ${oldpath} does not exist.`);
194
return;
195
}
196
openRenameModal(`Rename ${filename}`,
@@ -211,7 +211,7 @@ export const commands = {
211
212
export function registerCommand(name: string, keybind: string, command: () => void) {
213
if (commands[name]) {
214
- info(`Command "${name}" already exists, skipping.`)
214
+ info(`Command "${name}" already exists, skipping...`)
215
return;
216
}
217
commands[name] = { "keybind": keybind, "command": command }
src/config/config.ts
+1
@@ -52,6 +52,7 @@ export async function getShortcuts() {
52
for (const shortcut of shortcuts) {
53
// skip binding shorcuts that are disabled
54
if (shortcut.disabled === "true") {
55
+ info(`The keybind "${shortcut.keybind}" is disabled. Skipping and/or falling back to default...`);
56
continue;
57
}
58
const keybind = parseKeybind(shortcut.keybind);
src/config/themehandler.ts
+3
@@ -1,3 +1,4 @@
1
+import { info } from "tauri-plugin-log-api";
2
import { themes } from "./themes/themes";
3
4
export function getThemes() {
@@ -12,6 +13,7 @@ export function getThemes() {
13
14
const stylesheet = document.styleSheets[0].cssRules[0] as CSSStyleRule;
15
export async function loadTheme(name: string) {
16
+ info(`Loading theme: ${name}...`);
17
let json = await import(`../config/themes/${name.toLowerCase()}-theme.json`);
18
const theme = Object.entries(json.theme);
19
for (const entries of theme) {
@@ -30,6 +32,7 @@ export async function loadTheme(name: string) {
32
}
33
}
34
}
35
+ info("Theme loaded sucessfully.");
36
}
37
export function getThemeProperty(styleName: string) {
38
for (const style of stylesheet.style) {
src/lib/File.ts
+15
-10
@@ -4,7 +4,7 @@ import { tabs, addEditorTab, renameTab, closeTab, refreshTabs } from "./EditorTa
4
import { filetree } from "./FileTree.svelte";
5
import { watchImmediate } from "tauri-plugin-fs-watch-api";
6
import { openFileTree } from "./Sidebar.svelte";
7
-import { trace, error, warn } from "tauri-plugin-log-api";
7
+import { trace, error, warn, info } from "tauri-plugin-log-api";
8
9
export async function openFile() {
10
let newPath = await dialog.open() as string;
@@ -19,7 +19,11 @@ export const dirLoadFail = writable(false);
19
export async function openFolder() {
20
dirLoadFail.set(false);
21
let directory = await dialog.open({directory: true}) as string;
22
- if (!directory) return;
22
+ info(`Opening folder in: ${directory}`);
23
+ if (!directory) {
24
+ warn("Directory path is null. Aborting...");
25
+ return
26
+ };
27
dirToLoad.set(directory.split(path.sep).pop());
28
// if file path is not in the configured scope already, add it
29
// TODO: should configure this so it doesnt access restricted paths based on user permissions
@@ -66,15 +70,15 @@ async function updateTree(directory, updateType = "") {
70
error(error);
71
}
72
if (!tree || tree === undefined) {
69
- error("Cannot load directory");
73
+ error(`Cannot load directory: ${tree}.`);
74
75
cancelDirectoryLoad("Cannot load directory");
76
return null;
77
}
78
79
if (loadTime > 100) {
76
- error(`Directory load time was too long. Aborting...`);
77
- trace(`Cancelled load after ${loadTime} seconds`);
80
+ warn(`Directory load time was too long. Aborting...`);
81
+ trace(`Cancelled directory load after ${loadTime} seconds. Directory: ${tree}`);
82
83
cancelDirectoryLoad("Error: Directory load timeout.");
84
return null;
@@ -151,7 +155,7 @@ export async function moveFile(source: string, dest: string, file: string) {
155
tab.path = `${dest}${path.sep}${filename}`;
156
refreshTabs();
157
} catch (error) {
154
- console.error(error);
158
+ error(`Cannot move ${file} into ${dest}. Error: ${error}`);
159
}
160
}
161
@@ -193,6 +197,7 @@ export function updateSaveState(saved = true) {
197
}
198
199
export async function openInExplorer(path: string) {
200
+ trace(`Opening ${path} in system explorer...`);
201
invoke("open_in_explorer",{ path: path});
202
}
203
@@ -209,14 +214,14 @@ export async function createFolder(p) {
214
try {
215
await fs.createDir(p);
216
} catch (error) {
212
- console.log(error);
217
+ error(`Cannot create folder in path ${p}. Error: ${error}`);
218
}
219
}
220
export async function createFile(p) {
221
try {
217
- await fs.writeFile(p, "");
222
+ await invoke("write_file", {path: p, content: "", enc: "UTF-8", hasBom: false});
223
} catch (error) {
219
- console.log(error);
224
+ error(`Cannot create file in path ${p}. Error: ${error}`);
225
}
226
addEditorTab(p, p.split(path.sep).pop());
227
}
@@ -235,7 +240,7 @@ export async function renameFile(filename: string, oldpath: string) {
240
try {
241
await fs.renameFile(oldpath, newpath);
242
} catch (error) {
238
- error(error);
243
+ error(`Cannot rename ${oldpath}. Error: ${error}`);
244
return false;
245
}
246
let tab = get(tabs).find(t => t.active && t.isfile);
src/lib/Tab/Tab.ts
+2
-1
@@ -2,6 +2,7 @@ import { writable } from "svelte/store";
2
import Editor from "../Editor.svelte";
3
import { dialog, invoke } from "@tauri-apps/api";
4
import { saveFile } from "../File";
5
+import { warn } from "tauri-plugin-log-api";
6
7
export class Tab {
8
id = 0;
@@ -75,7 +76,7 @@ export class Tab {
76
// TODO: Fix performance issues/loading times on large files
77
fileData = await invoke("read_file", {path: path});
78
} catch (error) {
78
- console.warn("Can't read file content. Setting to empty string. Error: ", error);
79
+ warn(`Can't read file content in ${path}. Setting to empty string. Error: ${error}`);
80
}
81
let content = new Editor({target: document.getElementById("tabview"), props: {content: fileData.text}});
82
let tab = new this.Tab(this.id, label, content, path);