From 5c3b4448575a15b5fed46071192ddc9736cda298 Mon Sep 17 00:00:00 2001 From: dam Date: Sat, 11 May 2024 03:15:40 +0100 Subject: Fixed error messages and how they're displayed. --- ttt.jai | 220 +++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 113 insertions(+), 107 deletions(-) (limited to 'ttt.jai') diff --git a/ttt.jai b/ttt.jai index f0d22be..7fff02d 100644 --- a/ttt.jai +++ b/ttt.jai @@ -125,23 +125,24 @@ Layouts :: enum u8 { } -error_message: string; +error_string_builder: String_Builder; error_time_limit := Apollo_Time.{0, 0}; -print_error :: (format :string, args : .. Any) { +print_error :: (message: string, data: *void, info: Log_Info) { if TUI.is_active() == false { - print(format, ..args, to_standard_error = true); - print("\n"); + write_strings(message, "\n", to_standard_error = true); return; } - if error_message.data != null { - free(error_message.data); + if error_time_limit < current_time_monotonic() { + reset(*error_string_builder); } - error_message = sprint(format, args); - + else { + append(*error_string_builder, " | "); + } + append(*error_string_builder, message); error_time_limit = current_time_monotonic() + seconds_to_apollo(5); } @@ -170,7 +171,7 @@ draw_error_window :: () { print_character(#char " "); } TUI.set_cursor_position(pos_x + 1, pos_y + 1); - write_string(error_message); + write_builder(*error_string_builder, false); } trigger_autosave :: () { @@ -183,6 +184,14 @@ show_processing :: () { write_strings(TUI.Commands.DrawingMode, TUI.Drawings.Diamond, TUI.Commands.TextMode); } +hide_processing :: () { + TUI.set_cursor_position(1, 1); + TUI.using_style(style_default); + TUI.tui_write_string(TUI.Commands.DrawingMode); + TUI.tui_write_string(TUI.Drawings.CornerTL); + TUI.tui_write_string(TUI.Commands.TextMode); +} + // Returns true if string to_compare is equal to any of the other passed strings, false otherwise. is_equal_to_any :: (to_compare :string, test_a :string, test_b :string) -> bool { return to_compare == test_a || to_compare == test_b; @@ -306,7 +315,7 @@ add_task :: (db: *Database, task: *Task = null) -> task: *Task, index: s64 { array_add(*db.tasks, new_task); for * db.total_times { - < idx: s64 { +find_similar_task :: (db: *Database, task: Task, ignore_times := false) -> idx: s64 { compare_array :: (a: [] $T, b: [] T) -> int { for 0..min(a.count, b.count)-1 { if a[it] > b[it] return 1; @@ -408,7 +417,7 @@ find_similar_task :: (db: *Database, task: Task) -> idx: s64 { } for db.tasks { - if compare(xx task.name, xx it.name) == 0 && compare_array(task.times, it.times) == 0 { + if compare(xx task.name, xx it.name) == 0 && (ignore_times || compare_array(task.times, it.times) == 0) { return it_index; } } @@ -526,15 +535,12 @@ reset_database :: (db: *Database) { // Stores data from database into binary file. // Returns success. -store_database :: (db: Database, path: string) -> success: bool { +store_database :: (db: Database, path: string) -> success: bool #must { assert(xx path, ASSERT_NOT_EMPTY, "path"); // Open file. - file, open_success := file_open(path, for_writing = true); // log_errors: bool = true - if open_success == false { - print_error("Failed to open file '%' while storing database: ERROR_FROM_LOG", path); // TODO Get error from logger ?! - return false; - } + file, open_success := file_open(path, for_writing = true); + if open_success == false return false; defer file_close(*file); file_write(*file, DB_FILE_SIGN_STR); @@ -546,35 +552,30 @@ store_database :: (db: Database, path: string) -> success: bool { // Loads data from binary file into database. // Returns success. -load_database :: (db: *Database, path: string) -> success: bool { +load_database :: (db: *Database, path: string) -> success: bool #must { assert(db != null, ASSERT_NOT_NULL, "db"); assert(xx path, ASSERT_NOT_EMPTY, "path"); // Open file. - file, open_success := file_open(path); // log_errors: bool = true - if open_success == false { - print_error("Failed to open file '%' while loading database: ERROR_FROM_LOG", path); // TODO Get error from logger ?! - return false; - } + file, open_success := file_open(path); + if open_success == false then return false; defer file_close(*file); // Validate file signature. file_signature: [DB_FILE_SIGN_STR.count] u8; read_success := file_read(file, *file_signature, DB_FILE_SIGN_STR.count); - if read_success == false print_error("Failed to read file signature from '%'.", path); + if read_success == false log_error("Failed to read file signature."); if cast(string)file_signature != DB_FILE_SIGN_STR { - print_error("Invalid file signature."); + log_error("Invalid file signature while loading database.\n"); return false; } // Read database structure. read_success = file_read(file, db, size_of(Database)); - // TODO Use print_error or assert? if read_success == false { - print_error("Failed to read database info from '%'.", path); + log_error("Failed to read database info."); return false; } - assert(read_success == true, "Failed to read database info from '%'.", path); // Reserve database capacity for tasks. tasks_count := db.tasks.count; @@ -588,14 +589,20 @@ load_database :: (db: *Database, path: string) -> success: bool { // Make sure we are reading all the file. buffer: u8; success, bytes := file_read(file, *buffer, 1); - assert(bytes == 0, "Unexpected content found at the end of file '%'.", path); + if bytes > 0 { + log_error("Unexpected content found at the end of file '%'.", path); + return false; + } + + // Make sure we have a valid selected index. + if db.tasks.count > 0 && db.selected_idx < 0 then db.selected_idx = 0; return true; } // Exports data into CSV file. // Returns success. -export_to_csv :: (db: Database, path: string) -> success: bool { +export_to_csv :: (db: Database, path: string) -> success: bool #must { assert(xx path, ASSERT_NOT_EMPTY, "path"); auto_release_temp(); @@ -616,14 +623,12 @@ export_to_csv :: (db: Database, path: string) -> success: bool { name, it.times[0], it.times[1], it.times[2], it.times[3], it.times[4], it.times[5], it.times[6]); } - write_entire_file(path, *builder); - - return true; + return write_entire_file(path, *builder); } // Imports CSV file into database. // Returns success. -import_from_csv :: (db: *Database, path: string) -> bool { +import_from_csv :: (db: *Database, path: string) -> bool #must { // TODO Review code. assert(db != null, ASSERT_NOT_NULL, "db"); assert(xx path, ASSERT_NOT_EMPTY, "path"); @@ -649,7 +654,7 @@ import_from_csv :: (db: *Database, path: string) -> bool { csv := data; if success == false { - print_error("Failed to read file '%' while loading database: ERROR_FROM_LOG", path); // TODO Get error from logger ?! + log_error("Failed to read file '%' while loading database: ERROR_FROM_LOG", path); // TODO Get error from logger ?! return false; } @@ -746,15 +751,14 @@ import_from_csv :: (db: *Database, path: string) -> bool { // Appends task to the end of the CSV file. // Returns success. -append_to_csv :: (task: Task, path: string) -> success: bool { +append_to_csv :: (task: Task, path: string) -> success: bool #must { assert(xx path, ASSERT_NOT_EMPTY, "path"); + + auto_release_temp(); file, file_success := file_open(path, true, true); + if file_success == false then return false; defer file_close(*file); - if file_success == false { - //print_error("Failed to open file '%s' while appending to CSV: %s.", path, strerror(errno)); // TODO Show internal error or something - return false; - } file_size := file_length(file); file_set_position(file, file_size-1); @@ -763,14 +767,13 @@ append_to_csv :: (task: Task, path: string) -> success: bool { if (last_char != #char "\n") { file_write(*file, "\n"); } - - task_name := copy_temporary_string(xx task.name); // TODO Cleanup this temp mess. + + task_name := copy_temporary_string(xx task.name); replace_chars(task_name, ",", #char " "); csv_line := tprint("%,%,%,%,%,%,%,%\n", task_name, task.times[0], task.times[1], task.times[2], task.times[3], task.times[4], task.times[5], task.times[6]); - file_write(*file, csv_line); - - return true; + + return file_write(*file, csv_line); } // Selects task by index. @@ -832,7 +835,7 @@ layout_tasks_rows : int; is_terminal_too_small := true; -initialize_tui :: () { +initialize_user_interface :: () { // Normal layout. layouts[Layouts.NORMAL] = .{ @@ -881,7 +884,7 @@ initialize_tui :: () { col.alignment_offset = offset; } } - + assert(TUI.setup_terminal(), "Failed to setup TUI."); } @@ -926,8 +929,6 @@ dbg_average := 0; // DEBUG dbg_count := 0; // DEBUG buffer: String_Builder; // TODO draw_user_interface :: (db: *Database, layout: *Layout, redraw_all: bool = true) { - - auto_release_temp(); @@ -953,6 +954,7 @@ draw_user_interface :: (db: *Database, layout: *Layout, redraw_all: bool = true) // If not much is happening, we may just update the active task and it's times. if redraw_all == false { + if active_task == null then return; layout_idx := get_layout_index_from_day_index(now_week_day); @@ -1250,6 +1252,8 @@ main :: () { #if DEBUG_MEMORY { defer report_memory_leaks(); // TODO Remove after final debug sessions. } + + context.logger = print_error; defer free_memory(); @@ -1262,17 +1266,13 @@ main :: () { home_path, success_path := get_absolute_path(home_dir); // Returns temporary memory. if success_path == false { - print_error("Failed to find home directory '%'.", home_dir); + log_error("Failed to find home directory '%'.", home_dir); exit(1); } app_directory = join(home_path, "/", APP_FOLDER_NAME); db_file_path = join(app_directory, "/", DB_FILE_NAME); ar_file_path = join(app_directory, "/", AR_FILE_NAME); - - // TODO app data should be stored under: - // Windows: APPDATA (~/AppData/Roaming) - // Unix: XDG_DATA_HOME (~/.local/share) make_directory_if_it_does_not_exist(app_directory, recursive = true); } @@ -1280,14 +1280,14 @@ main :: () { { // Initialize database and archive files if needed. if (file_exists(db_file_path) == false) { if (store_database(database, db_file_path) == false) { - print_error("Failed to initialize database."); + log_error("Failed to initialize database."); exit(1); } } if (file_exists(ar_file_path) == false) { if (export_to_csv(archive, ar_file_path) == false) { - print_error("Failed to initialize archive."); + log_error("Failed to initialize archive."); exit(1); } } @@ -1367,19 +1367,19 @@ main :: () { if is_equal_to_any(args[it], "--import-csv", "-i") { it += 1; if it >= args.count { - print_error("Missing CSV file path to import."); + log_error("Missing CSV file path to import."); exit(1); } if (load_database(*database, db_file_path) == false) { - print_error("Failed to load database."); + log_error("Failed to load database during import."); exit(1); } if (import_from_csv(*database, args[it]) == false) { - print_error("Failed to import CSV file."); + log_error("Failed to import CSV file."); exit(1); } if (store_database(*database, db_file_path) == false) { - print_error("Failed to store database."); + log_error("Failed to store database during import."); exit(1); } reset_database(*database); @@ -1390,15 +1390,15 @@ main :: () { if is_equal_to_any(args[it], "--export-csv", "-e") { it += 1; if it >= args.count { - print_error("Missing CSV file path to export."); + log_error("Missing CSV file path to export."); exit(1); } if (load_database(*database, db_file_path) == false) { - print_error("Failed to load database."); + log_error("Failed to load database during export."); exit(1); } if (export_to_csv(*database, args[it]) == false) { - print_error("Failed to export CSV file."); + log_error("Failed to export CSV file."); exit(1); } reset_database(*database); @@ -1409,7 +1409,7 @@ main :: () { if is_equal_to_any(args[it], "--start-of-week", "-s") { it += 1; if it >= args.count { - print_error("Missing number for starting day of week."); + log_error("Missing number for starting day of week."); exit(1); } first_day_of_week = parse_int(*args[it]); @@ -1421,7 +1421,7 @@ main :: () { continue; } - print_error("%: invalid option '%'.\nTry '% --help' for more information.", args[0], args[it], args[0]); + log_error("%: invalid option '%'.\nTry '% --help' for more information.", args[0], args[it], args[0]); exit(1); } @@ -1431,11 +1431,11 @@ main :: () { } if (load_database(*database, db_file_path) == false) { - print_error("Failed to load database."); + log_error("Failed to load database."); exit(1); } - initialize_tui(); + initialize_user_interface(); db := *database; layout := *layouts[Layouts.COMPACT]; @@ -1511,9 +1511,14 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont if (countdown_to_autosave <= 0) { show_processing(); if (db == *archive) { - export_to_csv(*archive, ar_file_path); + if export_to_csv(*archive, ar_file_path) == false { + log_error("Failed to store archive during autosave."); + } + } + if store_database(database, db_file_path) == false { + log_error("Failed to store database during autosave."); } - store_database(database, db_file_path); + hide_processing(); } } @@ -1653,8 +1658,8 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont // Apply changes. time := cast(s64)input_time; day := get_day_index_from_layout_index(selected_day); - if is_assign set_task_time(db, db.selected_idx, day, time); - else add_task_time(db, db.selected_idx, day, time); + if is_assign set_task_time(db, db.selected_idx, day, time); + else add_task_time(db, db.selected_idx, day, time); trigger_autosave(); @@ -1691,10 +1696,7 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont continue; } - if (add_task(db, selected_task) == null) { - print_error("Failed to duplicate task."); - continue; - } + add_task(db, selected_task); trigger_autosave(); // Refresh totals. @@ -1721,14 +1723,14 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont if (db == *database) { if (import_from_csv(*archive, ar_file_path) == false) { reset_database(*archive); - print_error("Failed to load archive."); + log_error("Failed to load archive."); continue; } db = *archive; } else { if (export_to_csv(*archive, ar_file_path) == false) { - print_error("Failed to store archive."); + log_error("Failed to store archive."); continue; } reset_database(*archive); @@ -1741,7 +1743,7 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont if (db != *database || selected_task == null || selected_task == active_task) continue; if (append_to_csv(selected_task, ar_file_path) == false) { - print_error("Failed to archive task."); + log_error("Failed to archive task."); continue; } delete_task(db, db.selected_idx); @@ -1758,10 +1760,7 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont continue; } - if (add_task(*database, selected_task) == null) { - print_error("Failed to restore task."); - continue; - } + add_task(*database, selected_task); delete_task(db, db.selected_idx); trigger_autosave(); @@ -1773,7 +1772,7 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont show_processing(); sort_procedure: (a: Task, b: Task) -> s64; - active_task: Task = ifx db.active_idx >= 0 then db.tasks[db.active_idx] else .{}; + prev_active_task: Task = ifx db.active_idx >= 0 then db.tasks[db.active_idx] else .{}; if sort_by == { case #char "n"; #through; case #char "N"; @@ -1809,7 +1808,7 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont quick_sort(db.tasks, sort_procedure); if db.active_idx >= 0 { - db.active_idx = find_similar_task(db, active_task); + db.active_idx = find_similar_task(db, prev_active_task); } trigger_autosave(); @@ -1820,23 +1819,29 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont TUI.using_style(action_style); if (prompt_user_key(selected_task_row, "Press enter to archive duplicates and reset all.") != TUI.Keys.Enter) continue; show_processing(); - + + failed_to_archive := false; for db.tasks { - if (append_to_csv(it, ar_file_path) == false) { - print_error("Failed to archive task."); // TODO Improve this. + if append_to_csv(it, ar_file_path) { + reset_task_times(db, it_index); + } + else { + failed_to_archive = true; } - reset_task_times(db, it_index); } trigger_autosave(); + if failed_to_archive then log_error("Failed to archive tasks."); // Coalesce similar tasks. case #char "c"; #through; case #char "C"; - // TODO Active task is lost... if (db.tasks.count <= 0) continue; TUI.using_style(action_style); if (prompt_user_key(selected_task_row, "Press enter to coalesce similar tasks.") != TUI.Keys.Enter) continue; show_processing(); + + active_task_idx := db.active_idx; + prev_active_task: Task = ifx db.active_idx >= 0 then db.tasks[db.active_idx] else .{}; head_idx := 0; while head_idx < db.tasks.count - 1 { @@ -1852,6 +1857,10 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont } head_idx += 1; } + + if active_task_idx >= 0 { + db.active_idx = find_similar_task(db, prev_active_task, ignore_times = true); + } trigger_autosave(); case TUI.Keys.Home; @@ -1876,31 +1885,28 @@ TUI.tui_print("Average % us (% / % : % bytes) ---------", dbg_average/1000, cont // Save any unsaved changes. show_processing(); - error_saving := false; if (db == *archive) { - if (export_to_csv(archive, ar_file_path) == false) { - print_error("Failed to save archive."); - error_saving |= true; + while true { + if (export_to_csv(archive, ar_file_path) == false) { + log_error("Failed to save archive, retry?"); + draw_error_window(); + if TUI.get_key() == TUI.Keys.Escape then break; + } + else break; } } if (countdown_to_autosave > 0 || is_autosave_enabled == false) { - if (store_database(database, db_file_path) == false) { - print_error("Failed to save database."); - error_saving |= true; + while true { + if (store_database(database, db_file_path) == false) { + log_error("Failed to save database, retry?"); + draw_error_window(); + if TUI.get_key() == TUI.Keys.Escape then break; + } + else break; } } - if (error_saving) { - print_error("Press any key to close."); - draw_error_window(); - TUI.get_key(); - } assert(TUI.reset_terminal(), "Failed to reset TUI."); -#if DEBUG_MEMORY { return; -} else { - exit(xx ifx error_saving then 1 else 0); -} } - -- cgit v1.2.3