185 lines
4.0 KiB
C
185 lines
4.0 KiB
C
#include <bshell/format.h>
|
|
#include <fx/collections/array.h>
|
|
#include <fx/string.h>
|
|
#include <fx/type.h>
|
|
#include <fx/value-type.h>
|
|
|
|
static void initialise_table(
|
|
struct bshell_value_writer *w,
|
|
const fx_value *first_record)
|
|
{
|
|
bshell_table_ctx_init(&w->w_table_ctx, w->w_dest);
|
|
const fx_type *ty = fx_type_get_by_id(first_record->v_type);
|
|
bshell_table_ctx_prepare_columns_from_format(&w->w_table_ctx, ty, NULL);
|
|
bshell_table_ctx_print_headers(&w->w_table_ctx);
|
|
w->w_table_initialised = true;
|
|
}
|
|
|
|
static size_t write_iterable(
|
|
struct bshell_value_writer *w,
|
|
const fx_value *value)
|
|
{
|
|
size_t written = 0;
|
|
fx_array *array = NULL;
|
|
fx_value_get_object(value, &array);
|
|
|
|
const fx_iterator *it = fx_iterator_begin(array);
|
|
fx_foreach(v, it)
|
|
{
|
|
written += bshell_value_writer_write(w, v);
|
|
}
|
|
fx_iterator_unref(it);
|
|
|
|
return written;
|
|
}
|
|
|
|
static bool string_has_trailing_newline(const char *s)
|
|
{
|
|
long len = strlen(s);
|
|
if (len == 0) {
|
|
return false;
|
|
}
|
|
|
|
return s[len - 1] == '\n';
|
|
}
|
|
|
|
static size_t write_control_char(struct bshell_value_writer *w, fx_wchar c)
|
|
{
|
|
bool oneline = (w->w_flags & BSHELL_VALUE_WRITER_ONELINE) != 0;
|
|
size_t written = 1;
|
|
|
|
switch (c) {
|
|
case '\n':
|
|
if (oneline) {
|
|
fx_stream_write_cstr(w->w_dest, "\\n", &written);
|
|
} else {
|
|
fx_stream_write_char(w->w_dest, '\n');
|
|
}
|
|
break;
|
|
case '\r':
|
|
fx_stream_write_cstr(w->w_dest, "\\r", NULL);
|
|
break;
|
|
case '\t':
|
|
fx_stream_write_cstr(w->w_dest, "\\t", NULL);
|
|
break;
|
|
case '\b':
|
|
fx_stream_write_cstr(w->w_dest, "\\b", NULL);
|
|
break;
|
|
default:
|
|
written = 0;
|
|
break;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
static size_t write_string(struct bshell_value_writer *w, const fx_value *value)
|
|
{
|
|
const char *s = NULL;
|
|
size_t written = 0;
|
|
fx_value_get_cstr(value, &s);
|
|
bool trailing_newline = string_has_trailing_newline(s);
|
|
bool inhibit_newline
|
|
= (w->w_flags & BSHELL_VALUE_WRITER_NO_NEWLINE) != 0;
|
|
bool truncate = (w->w_flags & BSHELL_VALUE_WRITER_TRUNCATE) != 0;
|
|
|
|
for (written = 0; *s;) {
|
|
if (truncate && written == w->w_max_width - 1) {
|
|
fx_stream_write_char(w->w_dest, L'…');
|
|
written++;
|
|
break;
|
|
}
|
|
|
|
fx_wchar c = fx_wchar_utf8_codepoint_decode(s);
|
|
unsigned int stride = fx_wchar_utf8_codepoint_size(c);
|
|
if (stride == 0) {
|
|
break;
|
|
}
|
|
|
|
if (!fx_wchar_is_graph(c)) {
|
|
written += write_control_char(w, c);
|
|
goto next;
|
|
}
|
|
|
|
char cs[5] = {0};
|
|
fx_wchar_utf8_codepoint_encode(c, cs);
|
|
|
|
size_t n = 0;
|
|
fx_stream_write_cstr(w->w_dest, cs, &n);
|
|
written += n;
|
|
|
|
next:
|
|
s += stride;
|
|
}
|
|
|
|
if (!trailing_newline && !inhibit_newline) {
|
|
fx_stream_write_char(w->w_dest, '\n');
|
|
written++;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
static size_t write_value(struct bshell_value_writer *w, const fx_value *value)
|
|
{
|
|
const fx_type *ty = fx_type_get_by_id(value->v_type);
|
|
if (fx_value_is_type(value, FX_TYPE_STRING)
|
|
|| fx_value_is_type(value, FX_TYPE_CSTR)) {
|
|
return write_string(w, value);
|
|
}
|
|
|
|
if (fx_type_get_interface(ty, FX_TYPE_ITERABLE)) {
|
|
return write_iterable(w, value);
|
|
}
|
|
|
|
if (fx_type_is_value_type(value->v_type)) {
|
|
fx_stringstream_reset(w->w_tmp);
|
|
fx_value_to_string(value, w->w_tmp, NULL);
|
|
fx_stream_write_char(w->w_tmp, '\n');
|
|
size_t result = fx_stringstream_get_length(w->w_tmp);
|
|
fx_stream_write_cstr(
|
|
w->w_dest,
|
|
fx_stringstream_get_cstr(w->w_tmp),
|
|
NULL);
|
|
return result;
|
|
}
|
|
|
|
if (!w->w_table_initialised) {
|
|
initialise_table(w, value);
|
|
}
|
|
|
|
bshell_table_ctx_print_record(&w->w_table_ctx, value);
|
|
return 0;
|
|
}
|
|
|
|
void bshell_value_writer_init(
|
|
struct bshell_value_writer *w,
|
|
enum bshell_value_writer_flags flags,
|
|
size_t max_width,
|
|
fx_stream *dest)
|
|
{
|
|
memset(w, 0x00, sizeof *w);
|
|
w->w_dest = dest;
|
|
w->w_flags = flags;
|
|
w->w_max_width = max_width;
|
|
w->w_tmp = fx_stringstream_create();
|
|
}
|
|
|
|
size_t bshell_value_writer_write(
|
|
struct bshell_value_writer *w,
|
|
const fx_value *value)
|
|
{
|
|
return write_value(w, value);
|
|
}
|
|
|
|
void bshell_value_writer_cleanup(struct bshell_value_writer *w)
|
|
{
|
|
if (w->w_table_initialised) {
|
|
bshell_table_ctx_cleanup(&w->w_table_ctx);
|
|
}
|
|
|
|
fx_stringstream_unref(w->w_tmp);
|
|
|
|
w->w_table_initialised = false;
|
|
}
|