Packages

A simple Gleam library for pretty printing tabular data!

Current section

Files

Jump to
trellis src trellis.erl
Raw

src/trellis.erl

-module(trellis).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([style/2, table/1, param/1, with/2, to_string/1]).
-export_type([table/1, cell_content/0]).
-type table(FBD) :: {table,
list(trellis@column:column(FBD)),
list(FBD),
trellis@style:style()}.
-type cell_content() :: {cell_content, list(binary()), integer(), integer()}.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 40).
-spec style(table(FBH), trellis@style:style()) -> table(FBH).
style(Table, Style) ->
_record = Table,
{table, erlang:element(2, _record), erlang:element(3, _record), Style}.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 35).
-spec table(list(FBE)) -> table(FBE).
table(Rows) ->
{table, [], Rows, line}.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 51).
-spec param(fun((FBK) -> FBL)) -> fun((FBK) -> FBL).
param(F) ->
F.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 56).
-spec with(table(FBM), trellis@column:column(FBM)) -> table(FBM).
with(Table, Column) ->
{table, Columns, _, _} = Table,
_record = Table,
{table,
[Column | Columns],
erlang:element(3, _record),
erlang:element(4, _record)}.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 225).
-spec make_separator(
trellis@style:style(),
list(integer()),
boolean(),
boolean()
) -> binary().
make_separator(Style, Widths, Is_header, Is_top) ->
{style_guide,
_,
Horizontal,
Top_left,
Top_middle,
Top_right,
Middle_left,
Middle_right,
Bottom_left,
Bottom_middle,
Bottom_right,
Cross} = trellis@style:style(Style),
{Start, Middle, End} = case {Is_top, Is_header} of
{true, _} ->
{Top_left, Top_middle, Top_right};
{false, true} ->
{Middle_right, Cross, Middle_left};
{false, false} ->
{Bottom_left, Bottom_middle, Bottom_right}
end,
Line = begin
_pipe = Widths,
gleam@list:fold(
_pipe,
Start,
fun(Acc, Col) ->
<<<<Acc/binary,
(gleam@string:repeat(Horizontal, Col + 2))/binary>>/binary,
Middle/binary>>
end
)
end,
<<(gleam@string:slice(Line, 0, string:length(Line) - 1))/binary,
End/binary>>.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 261).
-spec pad_left(binary(), integer()) -> binary().
pad_left(Text, Width) ->
Padding = Width - string:length(Text),
<<(gleam@string:repeat(<<" "/utf8>>, Padding))/binary, Text/binary>>.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 267).
-spec pad_right(binary(), integer()) -> binary().
pad_right(Text, Width) ->
Padding = Width - string:length(Text),
<<Text/binary, (gleam@string:repeat(<<" "/utf8>>, Padding))/binary>>.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 273).
-spec pad_center(binary(), integer()) -> binary().
pad_center(Text, Width) ->
Padding = Width - string:length(Text),
Left = Padding div 2,
Right = Padding - Left,
<<<<(gleam@string:repeat(<<" "/utf8>>, Left))/binary, Text/binary>>/binary,
(gleam@string:repeat(<<" "/utf8>>, Right))/binary>>.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 129).
-spec make_multi_line_row(
trellis@style:style(),
list(trellis@column:align()),
list(integer()),
list(cell_content())
) -> list(binary()).
make_multi_line_row(Style, Aligns, Widths, Cell_contents) ->
Vertical = erlang:element(2, trellis@style:style(Style)),
Max_height = gleam@list:fold(
Cell_contents,
0,
fun(Acc, Cell) -> gleam@int:max(Acc, erlang:element(4, Cell)) end
),
_pipe = gleam@list:range(0, Max_height - 1),
gleam@list:map(
_pipe,
fun(Line_num) ->
Parts = gleam@list:map2(
gleam@list:zip(Aligns, Widths),
Cell_contents,
fun(Tup, Cell@1) ->
{erlang:element(1, Tup), erlang:element(2, Tup), Cell@1}
end
),
Line_parts = gleam@list:map(
Parts,
fun(Part) ->
{Align, Width, Cell@2} = Part,
Content = gleam@list:index_fold(
erlang:element(2, Cell@2),
<<""/utf8>>,
fun(Acc@1, Line, I) -> case Line_num =:= I of
true ->
Line;
_ ->
Acc@1
end end
),
Padded = case Align of
left ->
pad_right(Content, Width);
right ->
pad_left(Content, Width);
center ->
pad_center(Content, Width)
end,
<<<<" "/utf8, Padded/binary>>/binary, " "/utf8>>
end
),
<<<<Vertical/binary,
(gleam@string:join(Line_parts, Vertical))/binary>>/binary,
Vertical/binary>>
end
).
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 294).
-spec do_wrap(list(binary()), integer(), binary(), list(binary())) -> list(binary()).
do_wrap(Words, Max_width, Current, Lines) ->
case Words of
[] ->
case Current of
<<""/utf8>> ->
lists:reverse(Lines);
_ ->
lists:reverse([gleam@string:trim(Current) | Lines])
end;
[Word | Rest] ->
With_space = case Current of
<<""/utf8>> ->
Word;
_ ->
<<<<Current/binary, " "/utf8>>/binary, Word/binary>>
end,
case string:length(With_space) =< Max_width of
true ->
do_wrap(Rest, Max_width, With_space, Lines);
false ->
case Current of
<<""/utf8>> ->
do_wrap(Rest, Max_width, Word, Lines);
_ ->
do_wrap(
[Word | Rest],
Max_width,
<<""/utf8>>,
[gleam@string:trim(Current) | Lines]
)
end
end
end.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 328).
-spec calculate_min_width(binary()) -> integer().
calculate_min_width(Text) ->
_pipe = gleam@string:split(Text, <<" "/utf8>>),
gleam@list:fold(
_pipe,
0,
fun(Acc, Word) -> gleam@int:max(Acc, string:length(Word)) end
).
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 280).
-spec wrap_text(binary(), integer()) -> list(binary()).
wrap_text(Text, Max_width) ->
Min_width = calculate_min_width(Text),
Effective_width = gleam@int:max(Max_width, Min_width),
case string:length(Text) =< Effective_width of
true ->
[Text];
false ->
Words = gleam@string:split(Text, <<" "/utf8>>),
do_wrap(Words, Effective_width, <<""/utf8>>, [])
end.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 64).
-spec cell_content(binary(), gleam@option:option(integer())) -> cell_content().
cell_content(Text, Width) ->
Newline_splits = gleam@string:split(Text, <<"\n"/utf8>>),
Lines = case Width of
{some, Width@1} ->
gleam@list:flat_map(
Newline_splits,
fun(Line) -> wrap_text(Line, Width@1) end
);
none ->
Newline_splits
end,
Content_width = gleam@list:fold(
Lines,
0,
fun(Acc, Line@1) -> gleam@int:max(Acc, string:length(Line@1)) end
),
Final_width = case Width of
{some, Max} ->
gleam@int:min(Content_width, Max);
none ->
Content_width
end,
{cell_content, Lines, Final_width, erlang:length(Lines)}.
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 89).
-spec calculate_widths(
list(binary()),
list(binary()),
list(gleam@option:option(integer()))
) -> list(integer()).
calculate_widths(Headers, Values, Widths) ->
Header_cells = gleam@list:map2(Headers, Widths, fun cell_content/2),
Columns = gleam@list:sized_chunk(Values, erlang:length(Headers)),
Transposed_columns = gleam@list:transpose(Columns),
Min_column_widths = gleam@list:map(
Transposed_columns,
fun(Col_values) ->
gleam@list:fold(
Col_values,
0,
fun(Acc, Val) ->
gleam@int:max(Acc, calculate_min_width(Val))
end
)
end
),
Value_cells = gleam@list:map2(
Transposed_columns,
Widths,
fun(Col_values@1, Width) ->
gleam@list:map(
Col_values@1,
fun(Value) -> cell_content(Value, Width) end
)
end
),
gleam@list:map2(
gleam@list:zip(Header_cells, Widths),
gleam@list:zip(Value_cells, Min_column_widths),
fun(_use0, _use1) ->
{Header, Max_width} = _use0,
{Column_cells, Min_width} = _use1,
Natural_width = gleam@list:fold(
Column_cells,
erlang:element(3, Header),
fun(Acc@1, Cell) ->
gleam@int:max(Acc@1, erlang:element(3, Cell))
end
),
Width@1 = gleam@int:max(Natural_width, Min_width),
case Max_width of
{some, Max_width@1} ->
gleam@int:max(
Min_width,
gleam@int:min(Width@1, Max_width@1)
);
none ->
Width@1
end
end
).
-file("/home/ethanthoma/projects/trellis/src/trellis.gleam", 175).
-spec to_string(table(any())) -> binary().
to_string(Table) ->
{table, Columns, Rows, Style} = Table,
Columns@1 = lists:reverse(Columns),
Headers = gleam@list:map(
Columns@1,
fun(Column) -> erlang:element(2, Column) end
),
Aligns = gleam@list:map(
Columns@1,
fun(Column@1) -> erlang:element(3, Column@1) end
),
Max_widths = gleam@list:map(
Columns@1,
fun(Column@2) -> erlang:element(4, Column@2) end
),
Values = gleam@list:flat_map(
Rows,
fun(Row) ->
gleam@list:map(
Columns@1,
fun(Col) -> (erlang:element(5, Col))(Row) end
)
end
),
Widths = calculate_widths(Headers, Values, Max_widths),
Separator_top = make_separator(Style, Widths, true, true),
Separator_header = make_separator(Style, Widths, true, false),
Separator_bottom = make_separator(Style, Widths, false, false),
Header_cells = gleam@list:map2(Headers, Max_widths, fun cell_content/2),
Header_rows = make_multi_line_row(
Style,
gleam@list:map(
gleam@list:range(1, erlang:length(Columns@1)),
fun(_) -> center end
),
Widths,
Header_cells
),
Body_rows = gleam@list:flat_map(
Rows,
fun(Row@1) ->
Cell_contents = gleam@list:map2(
Columns@1,
Max_widths,
fun(Col@1, W) ->
cell_content((erlang:element(5, Col@1))(Row@1), W)
end
),
make_multi_line_row(Style, Aligns, Widths, Cell_contents)
end
),
_pipe = [Separator_top],
_pipe@1 = lists:append(_pipe, Header_rows),
_pipe@2 = lists:append(_pipe@1, [Separator_header]),
_pipe@3 = lists:append(_pipe@2, Body_rows),
_pipe@4 = lists:append(_pipe@3, [Separator_bottom]),
gleam@string:join(_pipe@4, <<"\n"/utf8>>).