ReportFormatter makes columnized reports given variable-width data lines. It does the hard work of automatically sizing columns and truncating data to fit the line width (unless all data fits the line which doesn’t happen often). This involves the following magic.
Internally, all column widths are first treated as percentages of the line width. Even if a column is specified with width=>N where N is some length of characters, this is converted to a percent/line width (rounded up).
Columns specified with width=>N or width_pct=>P (where P is some percent of total line width, not remaining line width when used with other width=>N columns) are fixed. You get exactly what you specify even if this results in the column header/name or values being truncated to fit. Otherwise, the column is “auto-width” and you get whatever the package gives you.
add_line() keeps track of min and max column values. When get_report() is called, it calls _calculate_column_widths() which begins the magic. It converts each column’s percentage width to characters, called the print width. So width_pct=>50 == print_width=>39 (characters). If the column is fixed (i.e. not auto-width) then print width is fixed. Otherwise, the print width is adjusted as follows.
The print width is set to the min val if, for some reason, it’s less than the min val. This is so the column is at least wide enough to print the minimum value. Else, if there’s a max val and the print val is wider than it, then the print val is set to the max val. This reclaims “extra space” from auto-width cols.
Extra space is distributed evenly among auto-width cols with print widths less than the column’s max val or header/name. This widens auto-width cols to either show longer values or truncate the column header/name less.
After these adjustments, get_report() calls _truncate_headers() and _truncate_line_values(). These truncate output to the columns’ final, calculated widths.