markdown_table.ry

module markdown_table
  purpose: Render rows of text as a Markdown table with aligned columns.

import std.console

public type Table
  purpose: A header row and any number of body rows.
  has header: List of Text
  has rows: List of List of Text
end

public function column_widths(table: Table) returns List of Integer
  purpose: The width of the widest cell in every column, the header included.
  example: column_widths(Table(header: ["name", "qty"], rows: [["apple", "1"]])) is [5, 3]

  return for each title, index in table.header.with_index()
    collect widest(table: table, index: index, minimum: title.length())
end

function widest(table: Table, index: Integer, minimum: Integer) returns Integer
  let mutable width be minimum
  for each row in table.rows
    let cell be row.at(index) otherwise ""
    if cell.length() is greater than width then change width to cell.length() end
  end
  return width
end

function render_row(cells: List of Text, widths: List of Integer) returns Text
  let mutable padded: List of Text be []
  for each width, index in widths.with_index()
    let cell be cells.at(index) otherwise ""
    change padded to padded.append(cell.pad_right(width))
  end
  let separator be " | "
  return "| {padded.join(separator)} |"
end

public function render(table: Table) returns Text
  purpose: The table as Markdown, every column padded to its widest cell.
  tags: markdown, formatting
  example: render(Table(header: ["name", "qty"], rows: [["apple", "1"], ["kiwi", "12"]])) is """
    | name  | qty |
    | ----- | --- |
    | apple | 1   |
    | kiwi  | 12  |
    """

  let widths be column_widths(table)
  let divider be for each width in widths collect "-".repeat(width)
  let mutable lines be [render_row(cells: table.header, widths: widths)]
  change lines to lines.append(render_row(cells: divider, widths: widths))
  for each row in table.rows
    change lines to lines.append(render_row(cells: row, widths: widths))
  end
  return lines.join("\n")
end

public function main() needs console
  purpose: Print a small table of fruit prices.

  let prices be Table(
    header: ["fruit", "price", "stock"],
    rows: [["apple", "0.50", "120"], ["kiwi", "0.80", "7"], ["watermelon", "3.20", "2"]]
  )
  console.print(render(prices))
end