#include #include #include #include #include #include #include #include static fx_namemap bshell_tables = FX_NAMEMAP_INIT; extern struct bshell_table hashtable_bshell_table; extern struct bshell_table verb_bshell_table; extern struct bshell_table command_bshell_table; enum bshell_status bshell_table_init(void) { fx_namemap_put( &bshell_tables, "fx.collections.hashtable", &hashtable_bshell_table.fmt_entry); fx_namemap_put( &bshell_tables, "bshell.verb", &verb_bshell_table.fmt_entry); fx_namemap_put( &bshell_tables, "bshell.command", &command_bshell_table.fmt_entry); return BSHELL_SUCCESS; } const struct bshell_table *bshell_table_get_for_type(const fx_type *ty) { while (ty) { const char *name = fx_type_get_name(ty); fx_namemap_entry *entry = fx_namemap_get(&bshell_tables, name); if (entry) { return fx_unbox(struct bshell_table, entry, fmt_entry); } ty = fx_type_get_parent(ty); } return NULL; } enum bshell_status format_object_table(fx_object *object, fx_stream *dest) { const fx_type *type = fx_type_get_by_id(fx_object_query_type(object)); if (!type) { return BSHELL_ERR_NOT_SUPPORTED; } const struct bshell_table *fmt = bshell_table_get_for_type(type); if (!fmt) { return BSHELL_ERR_NOT_SUPPORTED; } struct bshell_table_ctx ctx; bshell_table_ctx_init(&ctx, dest); const fx_iterator *it = fx_iterator_begin(object); if (!it) { bshell_table_ctx_prepare_columns_from_format( &ctx, fx_type_get_by_id(fx_object_query_type(object)), fmt); bshell_table_ctx_print_headers(&ctx); bshell_table_ctx_print_record(&ctx, &FX_VALUE_OBJECT(object)); } else { const fx_value *first = fx_iterator_get_value(it); if (first) { bshell_table_ctx_prepare_columns_from_format( &ctx, fx_type_get_by_id(first->v_type), fmt); } bshell_table_ctx_print_headers(&ctx); fx_foreach(value, it) { bshell_table_ctx_print_record(&ctx, value); } } bshell_table_ctx_cleanup(&ctx); return BSHELL_SUCCESS; } enum bshell_status bshell_table_ctx_init( struct bshell_table_ctx *ctx, fx_stream *out) { memset(ctx, 0x0, sizeof *ctx); ctx->ctx_out = out; return BSHELL_SUCCESS; } enum bshell_status bshell_table_ctx_cleanup(struct bshell_table_ctx *ctx) { if (ctx->ctx_columns) { free(ctx->ctx_columns); } memset(ctx, 0x0, sizeof *ctx); return BSHELL_SUCCESS; } enum bshell_status bshell_table_ctx_prepare_columns_from_format( struct bshell_table_ctx *ctx, const fx_type *record_type, const struct bshell_table *fmt) { if (!fmt && !record_type) { return BSHELL_ERR_INVALID_ARGUMENT; } if (!fmt) { fmt = bshell_table_get_for_type(record_type); } if (!fmt) { return bshell_table_ctx_prepare_columns_from_object( ctx, record_type); } ctx->ctx_fmt = fmt; struct bshell_table_ctx_column *columns = calloc(fmt->fmt_column_count, sizeof *columns); if (!columns) { return BSHELL_ERR_NO_MEMORY; } const struct bshell_table_column *column_defs = fmt->fmt_columns; ctx->ctx_columns = columns; ctx->ctx_columns_count = fmt->fmt_column_count; fx_tty_get_dimensions(fx_stdtty, &ctx->ctx_tty_width, NULL); if (!ctx->ctx_tty_width) { ctx->ctx_tty_width = 150; } ctx->ctx_variable_columns_count = 0; unsigned int tty_width = ctx->ctx_tty_width; for (size_t i = 0; i < fmt->fmt_column_count; i++) { columns[i].col_property = fx_type_get_property( record_type, column_defs[i].col_property_name); columns[i].col_title = column_defs[i].col_display_name; columns[i].col_title_ptr = column_defs[i].col_display_name; if (column_defs[i].col_width == COL_WIDTH_INFINITE) { ctx->ctx_variable_columns_count++; continue; } if (column_defs[i].col_width <= tty_width) { columns[i].col_width = column_defs[i].col_width; tty_width -= columns[i].col_width; continue; } columns[i].col_width = tty_width; tty_width = 0; break; } for (size_t i = 0; i < fmt->fmt_column_count; i++) { if (column_defs[i].col_width == COL_WIDTH_INFINITE) { columns[i].col_width = tty_width / ctx->ctx_variable_columns_count; } } return BSHELL_SUCCESS; } enum bshell_status bshell_table_ctx_prepare_columns_from_object( struct bshell_table_ctx *ctx, const fx_type *record_type) { fx_array *properties = fx_array_create(); fx_iterator *it = fx_type_get_properties(record_type); fx_foreach(prop_v, it) { fx_array_push_back(properties, *prop_v); } fx_iterator_unref(it); size_t column_count = fx_array_get_size(properties); if (!column_count) { fx_array_unref(properties); return BSHELL_SUCCESS; } struct bshell_table_ctx_column *columns = calloc(column_count, sizeof *columns); if (!columns) { fx_array_unref(properties); return BSHELL_ERR_NO_MEMORY; } ctx->ctx_columns = columns; ctx->ctx_columns_count = column_count; fx_tty_get_dimensions(fx_stdtty, &ctx->ctx_tty_width, NULL); if (!ctx->ctx_tty_width) { ctx->ctx_tty_width = 150; } ctx->ctx_variable_columns_count = 0; unsigned int tty_width = ctx->ctx_tty_width; for (size_t i = 0; i < column_count; i++) { fx_value *prop_v = fx_array_get_ref(properties, i); fx_property *prop = NULL; fx_value_get_object(prop_v, &prop); columns[i].col_property = prop; columns[i].col_title = fx_property_get_name(prop); columns[i].col_title_ptr = columns[i].col_title; columns[i].col_width = tty_width / column_count; } return BSHELL_SUCCESS; } enum bshell_status bshell_table_ctx_print_headers(struct bshell_table_ctx *ctx) { fx_printf("[bold,bright_green]"); bool repeat = false; struct bshell_table_ctx_column *c = NULL; do { repeat = false; for (size_t i = 0; i < ctx->ctx_columns_count; i++) { c = &ctx->ctx_columns[i]; if (c->col_width == 0) { continue; } size_t title_chars_remaining = strlen(c->col_title_ptr); size_t to_write = title_chars_remaining; if (to_write > c->col_width) { to_write = c->col_width; } size_t x; for (x = 0; x < to_write; x++) { fx_stream_write_char( ctx->ctx_out, *c->col_title_ptr); c->col_title_ptr++; } for (; x < c->col_width; x++) { fx_stream_write_char(ctx->ctx_out, ' '); } if (to_write != title_chars_remaining) { repeat = true; } } } while (repeat); fx_stream_write_char(ctx->ctx_out, '\n'); for (size_t i = 0; i < ctx->ctx_columns_count; i++) { struct bshell_table_ctx_column *c = &ctx->ctx_columns[i]; if (c->col_width == 0) { continue; } size_t title_len = strlen(c->col_title); if (title_len > c->col_width) { title_len = c->col_width; } size_t x; for (x = 0; x < title_len; x++) { fx_stream_write_char(ctx->ctx_out, '-'); } for (; x < c->col_width; x++) { fx_stream_write_char(ctx->ctx_out, ' '); } } fx_printf("[reset]"); fx_stream_write_char(ctx->ctx_out, '\n'); return BSHELL_SUCCESS; } enum bshell_status bshell_table_ctx_print_record( struct bshell_table_ctx *ctx, const fx_value *record) { fx_stringstream *strm = fx_stringstream_create(); for (size_t i = 0; i < ctx->ctx_columns_count; i++) { struct bshell_table_ctx_column *c = &ctx->ctx_columns[i]; if (c->col_width == 0) { continue; } fx_stringstream_reset(strm); fx_value column_value = FX_VALUE_EMPTY; if (c->col_property) { fx_status status = fx_property_get_value( c->col_property, record, &column_value); if (FX_OK(status)) { fx_value_to_string(&column_value, strm, NULL); } } size_t to_write = fx_stringstream_get_length(strm); const char *s = fx_stringstream_ptr(strm); bool overflow = false; if (to_write > c->col_width) { to_write = c->col_width - 2; overflow = true; } size_t x; for (x = 0; x < to_write; x++) { fx_stream_write_char(ctx->ctx_out, s[x]); } if (overflow) { fx_stream_write_char(ctx->ctx_out, L'…'); x++; } for (; x < c->col_width; x++) { fx_stream_write_char(ctx->ctx_out, ' '); } } fx_stream_write_char(ctx->ctx_out, '\n'); fx_stringstream_unref(strm); return BSHELL_SUCCESS; }