@setoelkahfi / svara / commits / c140d82

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);