From 0ab1855105fbc266766ced1aa06b6a1202eff1c6 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 28 Jul 2025 22:21:13 +0100 Subject: [PATCH] term: add an enhanced error reporting function --- term/error.c | 369 +++++++++++++++++++++++++++++++++ term/include/blue/term/print.h | 13 +- 2 files changed, 379 insertions(+), 3 deletions(-) create mode 100644 term/error.c diff --git a/term/error.c b/term/error.c new file mode 100644 index 0000000..11af642 --- /dev/null +++ b/term/error.c @@ -0,0 +1,369 @@ +#include +#include +#include +#include + +static void get_error_id( + const struct b_error_vendor *vendor, const struct b_error *error, + char *out, size_t max) +{ + const char *vendor_name = NULL; + const char *error_name = NULL; + b_error_status_code code = b_error_get_status_code(error); + + if (vendor) { + vendor_name = vendor->v_name; + error_name = b_error_vendor_get_status_code_name(vendor, code); + } + + b_stringstream id; + b_stringstream_begin(&id, out, max); + + if (vendor_name) { + b_stringstream_add(&id, vendor_name); + } + + if (error_name) { + if (vendor_name) { + b_stringstream_add(&id, "."); + } + + b_stringstream_add(&id, error_name); + } else { + if (vendor_name) { + b_stringstream_add(&id, "#"); + } + + b_stringstream_addf(&id, "%ld", code); + } +} + +static void print_template_parameter( + const struct b_error_template_parameter *params, size_t nr_params, + const char *param_name) +{ + const struct b_error_template_parameter *param = NULL; + + for (size_t i = 0; i < nr_params; i++) { + if (!params[i].param_name) { + break; + } + + if (!strcmp(params[i].param_name, param_name)) { + param = ¶ms[i]; + break; + } + } + + if (!param) { + return; + } + + const char *format = param->__param_def->param_format; + + switch (param->__param_def->param_type) { + case B_ERROR_TEMPLATE_PARAM_STRING: { + const char *s = (const char *)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, s); + break; + } + case B_ERROR_TEMPLATE_PARAM_CHAR: { + char v = (char)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_INT: { + int v = (int)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_UINT: { + unsigned int v = (unsigned int)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_LONG: { + long v = (long)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_ULONG: { + unsigned long v = (unsigned long)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_LONGLONG: { + long long v = (long long)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_ULONGLONG: { + unsigned long long v = (unsigned long long)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_PTR: { + const void *p = (const void *)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, p); + break; + } + case B_ERROR_TEMPLATE_PARAM_INTPTR: { + intptr_t v = (intptr_t)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + case B_ERROR_TEMPLATE_PARAM_UINTPTR: { + uintptr_t v = (uintptr_t)param->param_value; + b_tty_printf(b_stdtty_err, 0, format, v); + break; + } + default: + return; + } +} + +static void print_content( + const struct b_error_template_parameter *params, size_t nr_params, + const char *s) +{ + char modifier = 0; + char buf[128]; + size_t buf_len = 0; + + size_t i; + for (i = 0; s[i];) { + char c = s[i]; + + if (c != '@') { + b_tty_putc(b_stdtty_err, 0, c); + i++; + continue; + } + + i++; + modifier = s[i]; + + if (!modifier) { + break; + } + + if (modifier != '[' && modifier != '{') { + i++; + } + + if (s[i] == '@') { + b_tty_putc(b_stdtty_err, 0, c); + break; + } + + bool template_param; + char end = 0; + + switch (s[i]) { + case '{': + template_param = false; + end = '}'; + break; + case '[': + template_param = true; + end = ']'; + break; + default: + return; + } + + switch (modifier) { + case 'i': + b_tty_printf(b_stdtty_err, 0, "[cyan]"); + break; + case 'w': + b_tty_printf(b_stdtty_err, 0, "[yellow]"); + break; + case 'e': + b_tty_printf(b_stdtty_err, 0, "[red]"); + break; + default: + break; + } + + buf_len = 0; + i++; + while (s[i] && s[i] != end) { + buf[buf_len++] = s[i]; + buf[buf_len] = 0; + + i++; + } + + if (template_param) { + print_template_parameter(params, nr_params, buf); + } else { + b_tty_printf(b_stdtty_err, 0, "%s", buf); + } + + b_tty_printf(b_stdtty_err, 0, "[reset]"); + + if (s[i] != 0) { + i++; + } + } +} + +static void print_submsg(const struct b_error *error) +{ + const struct b_error_definition *error_def = b_error_get_definition(error); + const struct b_error_submsg *submsg = b_error_get_first_submsg(error); + + while (submsg) { + enum b_error_submsg_type type = b_error_submsg_get_type(submsg); + const char *content = b_error_submsg_get_content(submsg); + const struct b_error_msg *msg = b_error_submsg_get_msg(submsg); + const struct b_error_template_parameter *params + = b_error_submsg_get_template_parameters(submsg); + + b_tty_printf(b_stdtty_err, 0, " "); + + switch (type) { + case B_ERROR_SUBMSG_ERROR: + b_tty_printf( + b_stdtty_err, 0, "[bright_red,bold]>[reset] "); + break; + case B_ERROR_SUBMSG_WARNING: + b_tty_printf( + b_stdtty_err, 0, + "[bright_yellow,bold]>[reset] "); + break; + case B_ERROR_SUBMSG_INFO: + b_tty_printf( + b_stdtty_err, 0, "[bright_cyan,bold]>[reset] "); + break; + default: + break; + } + + if (msg) { + print_content( + params, B_ERROR_TEMPLATE_PARAMETER_MAX, + b_error_msg_get_content(msg)); + } else if (content) { + print_content( + params, B_ERROR_TEMPLATE_PARAMETER_MAX, content); + } + + b_tty_printf(b_stdtty_err, 0, "\n"); + + submsg = b_error_get_next_submsg(error, submsg); + } +} + +const char *get_short_filepath(const char *path) +{ + size_t len = strlen(path); + unsigned int sep_count = 0; + + for (size_t i = len - 1; i > 0; i--) { + if (path[i] != '/' && path[i] != '\\') { + continue; + } + + sep_count++; + if (sep_count == 2) { + return path + i + 1; + } + } + + return path; +} + +static void print_stack_trace(const struct b_error *error) +{ + const struct b_error_stack_frame *frame + = b_error_get_first_stack_frame(error); + while (frame) { + const char *file, *func; + unsigned int line; + + file = b_error_stack_frame_get_filepath(frame); + line = b_error_stack_frame_get_line_number(frame); + func = b_error_stack_frame_get_function_name(frame); + + file = get_short_filepath(file); + + b_tty_printf( + b_stdtty_err, 0, + " [dark_grey]at %s() (%s:%u)[reset]\n", func, file, + line); + + frame = b_error_get_next_stack_frame(error, frame); + } +} + +static void report_error( + const b_error *error, b_error_report_flags flags, bool caused_by) +{ + const b_error_vendor *vendor = b_error_get_vendor(error); + + char error_id[128]; + get_error_id(vendor, error, error_id, sizeof error_id); + + const struct b_error_definition *error_def = b_error_get_definition(error); + b_error_status_code code = b_error_get_status_code(error); + const char *description = b_error_get_description(error); + const struct b_error_template_parameter *params + = b_error_get_template_parameters(error); + + if (!description && vendor) { + description = b_error_vendor_get_status_code_description( + vendor, code); + } + + if (caused_by) { + b_tty_printf( + b_stdtty_err, 0, + " [green]->[reset] caused by [bright_red,bold]ERROR "); + } else { + b_tty_printf(b_stdtty_err, 0, "[bright_red,bold]==> ERROR "); + } + + if (flags & B_ERROR_REPORT_STATUS) { + b_tty_printf(b_stdtty_err, 0, "%s", error_id); + } + + b_tty_printf(b_stdtty_err, 0, "[reset]"); + + if (flags & B_ERROR_REPORT_DESCRIPTION) { + const struct b_error_msg *msg = b_error_get_msg(error); + if (msg) { + b_tty_printf(b_stdtty_err, 0, ": "); + print_content( + params, B_ERROR_TEMPLATE_PARAMETER_MAX, + b_error_msg_get_content(msg)); + } else if (description) { + b_tty_printf(b_stdtty_err, 0, ": "); + print_content( + params, B_ERROR_TEMPLATE_PARAMETER_MAX, + description); + } + } + + b_tty_printf(b_stdtty_err, 0, "\n"); + + if (flags & B_ERROR_REPORT_SUBMSG) { + print_submsg(error); + } + + if (flags & B_ERROR_REPORT_STACK_TRACE) { + print_stack_trace(error); + } + + const struct b_error *cause = b_error_get_caused_by(error); + if (cause && (flags & B_ERROR_REPORT_CAUSE)) { + report_error(cause, flags, true); + } +} + +void b_enhanced_error_reporter( + const struct b_error *error, b_error_report_flags flags) +{ + report_error(error, flags, false); +} diff --git a/term/include/blue/term/print.h b/term/include/blue/term/print.h index 9627d20..8eda77c 100644 --- a/term/include/blue/term/print.h +++ b/term/include/blue/term/print.h @@ -12,6 +12,10 @@ struct b_tty; +struct b_error; +struct b_error_vendor; +enum b_error_report_flags; + typedef enum b_paragraph_format_flags { B_PARAGRAPH_DONT_INDENT_FIRST_LINE = 0x01u, B_PARAGRAPH_HYPHENATE = 0x02u, @@ -39,7 +43,10 @@ BLUE_API b_status b_print_paragraph( const char *str, struct b_tty *tty, b_paragraph_format *format); BLUE_API int b_putc(char c); -BLUE_API int b_puts(const char* s); -BLUE_API int b_printf(const char* format, ...); +BLUE_API int b_puts(const char *s); +BLUE_API int b_printf(const char *format, ...); -#endif \ No newline at end of file +BLUE_API void b_enhanced_error_reporter( + const struct b_error *, enum b_error_report_flags flags); + +#endif