term: add an enhanced error reporting function

This commit is contained in:
2025-07-28 22:21:13 +01:00
parent 663df15289
commit 0ab1855105
2 changed files with 379 additions and 3 deletions

369
term/error.c Normal file
View File

@@ -0,0 +1,369 @@
#include <blue/core/error.h>
#include <blue/core/stringstream.h>
#include <blue/term/tty.h>
#include <inttypes.h>
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 = &params[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);
}