defmodule Scribe.Formatter.Index do @moduledoc false defstruct row: 0, row_max: 0, col: 0, col_max: 0 end defmodule Scribe.Formatter.Line do @moduledoc false defstruct data: [], widths: [], style: nil, opts: [], index: nil alias Scribe.Formatter.{Index, Line} def format(%Line{index: %Index{row: 0}} = line) do top(line) <> data(line) <> bottom(line) end def format(%Line{} = line) do data(line) <> bottom(line) end def data(%Line{} = line) do %Line{ data: row, widths: widths, style: style, opts: opts, index: index } = line border = style.border_at(index.row, 0, index.row_max, index.col_max) left_edge = border.left_edge line = Enum.reduce(0..(index.col_max - 1), "", fn x, acc -> b = style.border_at(index.row, x, index.row_max, index.col_max) width = Enum.at(widths, x) value = Enum.at(row, x) cell_value = case opts[:colorize] do false -> value |> cell(width, opts[:alignment]) _ -> value |> cell(width, opts[:alignment]) |> colorize(style.color(value)) end acc <> cell_value <> b.right_edge end) left_edge <> line <> "\n" end def cell(x, width, alignment \\ :left) do len = min(String.length(" #{inspect(x)} "), width) case alignment do :center -> padding = String.duplicate(" ", div(width - len, 2)) remaining = String.duplicate(" ", rem(width - len, 2)) truncate(" #{padding}#{inspect(x)}#{padding}#{remaining}", width - 2) <> " " :right -> padding = String.duplicate(" ", width - len) truncate(" #{padding}#{inspect(x)}", width - 2) <> " " _ -> padding = String.duplicate(" ", width - len) truncate(" #{inspect(x)}#{padding}", width - 2) <> " " end end def cell_value(x, padding, max_width) when padding >= 0 do truncate(" #{inspect(x)}#{String.duplicate(" ", padding)} ", max_width) end defp truncate(elem, width) do String.slice(elem, 0..width) end def colorize(string, color) do "#{color}#{string}#{IO.ANSI.reset()}" end def top(%Line{widths: widths, style: style, index: index, opts: opts}) do border = style.border_at(index.row, 0, index.row_max, index.col_max) top_left = border.top_left_corner line = Enum.reduce(0..(index.col_max - 1), "", fn x, acc -> b = style.border_at(index.row, x, index.row_max, index.col_max) width = Enum.at(widths, x) acc <> String.duplicate(b.top_edge, width) <> b.top_right_corner end) color_prefix = if Keyword.get(opts, :colorize, true) do style.default_color() else "" end color_prefix <> top_left <> add_newline(line) end def bottom(%Line{widths: widths, style: style, index: index}) do border = style.border_at(index.row, 0, index.row_max, index.col_max) bottom_left = border.bottom_left_corner line = Enum.reduce(0..(index.col_max - 1), "", fn x, acc -> b = style.border_at(index.row, x, index.row_max, index.col_max) width = Enum.at(widths, x) acc <> String.duplicate(b.bottom_edge, width) <> b.bottom_right_corner end) bottom_left <> add_newline(line) end def add_newline(""), do: "" def add_newline(line), do: line <> "\n" end