module Tabulo # @!visibility private class Border Style = Struct.new( :corner_top_left, :corner_top_right, :corner_bottom_right, :corner_bottom_left, :edge_top, :edge_right, :edge_bottom, :edge_left, :tee_top, :tee_right, :tee_bottom, :tee_left, :divider_vertical, :divider_horizontal, :intersection) STYLES = { ascii: Style.new( "+", "+", "+", "+", "-", "|", "-", "|", "+", "+", "+", "+", "|", "-", "+", ), classic: Style.new( "+", "+", "", "", "-", "|", "", "|", "+", "+", "", "+", "|", "-", "+", ), reduced_ascii: Style.new( "", "", "", "", "-", "", "-", "", " ", "", " ", "", " ", "-", " ", ), reduced_modern: Style.new( "", "", "", "", "─", "", "─", "", " ", "", " ", "", " ", "─", " ", ), markdown: Style.new( "", "", "", "", "", "|", "", "|", "", "|", "", "|", "|", "-", "|", ), modern: Style.new( "┌", "┐", "┘", "└", "─", "│", "─", "│", "┬", "┤", "┴", "├", "│", "─", "┼", ), blank: Style.new( "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ), } # @!visibility private def self.from(initializer, styler = nil) new(**options(initializer).merge(styler: styler)) end # @!visibility private def horizontal_rule(column_widths, position = :bottom) left, center, right, segment = case position when :title_top [@corner_top_left, @edge_top, @corner_top_right, @edge_top] when :title_bottom [@tee_left, @tee_top, @tee_right, @edge_top] when :top [@corner_top_left, @tee_top, @corner_top_right, @edge_top] when :middle [@tee_left, @intersection, @tee_right, @divider_horizontal] when :bottom [@corner_bottom_left, @tee_bottom, @corner_bottom_right, @edge_bottom] end segments = column_widths.map { |width| segment * width } # Prevent weird bottom edge of title if segments empty but right/left not empty, as in # Markdown border. left = right = "" if segments.all?(&:empty?) style("#{left}#{segments.join(center)}#{right}") end # @!visibility private def join_cell_contents(cells) styled_divider_vertical = style(@divider_vertical) styled_edge_left = style(@edge_left) styled_edge_right = style(@edge_right) styled_edge_left + cells.join(styled_divider_vertical) + styled_edge_right end private def self.options(kind) opts = STYLES[kind] return opts.to_h if opts raise InvalidBorderError end # @param [nil, #to_proc] styler (nil) A lambda or other callable object taking # a single parameter, representing a section of the table's borders (which for this purpose # include any horizontal and vertical lines inside the table), and returning a string. # If passed nil, then no additional styling will be applied to borders. If passed a # callable, then that callable will be called for each border section, with the # resulting string rendered in place of that border. The extra width of the string returned by the # {styler} is not taken into consideration by the internal table rendering calculations # Thus it can be used to apply ANSI escape codes to border characters, to colour the borders # for example, without breaking the table formatting. # @return [Border] a new {Border} def initialize( corner_top_left: "", corner_top_right: "", corner_bottom_right: "", corner_bottom_left: "", edge_top: "", edge_right: "", edge_bottom: "", edge_left: "", tee_top: "", tee_right: "", tee_bottom: "", tee_left: "", divider_vertical: "", divider_horizontal: "", intersection: "", styler: nil) @corner_top_left = corner_top_left @corner_top_right = corner_top_right @corner_bottom_right = corner_bottom_right @corner_bottom_left = corner_bottom_left @edge_top = edge_top @edge_right = edge_right @edge_bottom = edge_bottom @edge_left = edge_left @tee_top = tee_top @tee_right = tee_right @tee_bottom = tee_bottom @tee_left = tee_left @divider_vertical = divider_vertical @divider_horizontal = divider_horizontal @intersection = intersection @styler = styler end def style(s) (@styler && !s.empty?) ? @styler.call(s) : s end end end