Skip to content

Commit

Permalink
Land #68, Allow opting out of whitespace removal
Browse files Browse the repository at this point in the history
  • Loading branch information
smcintyre-r7 committed Feb 22, 2024
2 parents 1a7b639 + 67a084f commit 1ef344b
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 18 deletions.
16 changes: 4 additions & 12 deletions lib/rex/text/table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,21 +21,13 @@ class Table
# To enforce all tables to be wrapped to the terminal's current width, call `Table.wrap_tables!`
# before invoking `Table.new` as normal.
def self.new(*args, &block)
if wrap_table?(args)
table_options = args.first
return ::Rex::Text::WrappedTable.new(table_options)
end
return super(*args, &block)
end

def self.wrap_table?(args)
return false unless wrapped_tables?
return super(*args, &block) unless wrap_table?(args)

table_options = args.first
if table_options&.key?('WordWrap')
return table_options['WordWrap']
end
::Rex::Text::WrappedTable.new(table_options)
end

def self.wrap_table?(args)
wrapped_tables?
end

Expand Down
11 changes: 9 additions & 2 deletions lib/rex/text/wrapped_table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def initialize(opts = {})
self.rows = []

self.width = opts['Width'] || ::IO.console&.winsize&.[](1) || ::BigDecimal::INFINITY
self.word_wrap = opts.fetch('WordWrap', true)
self.indent = opts['Indent'] || 0
self.cellpad = opts['CellPad'] || 2
self.prefix = opts['Prefix'] || ''
Expand All @@ -87,6 +88,7 @@ def initialize(opts = {})
self.colprops[idx] = {}
self.colprops[idx]['Width'] = nil
self.colprops[idx]['WordWrap'] = true
self.colprops[idx]['Strip'] = true
self.colprops[idx]['Stylers'] = []
self.colprops[idx]['Formatters'] = []
self.colprops[idx]['ColumnStylers'] = []
Expand Down Expand Up @@ -328,7 +330,7 @@ def [](*col_names)

attr_accessor :header, :headeri # :nodoc:
attr_accessor :columns, :rows, :colprops # :nodoc:
attr_accessor :width, :indent, :cellpad # :nodoc:
attr_accessor :width, :word_wrap, :indent, :cellpad # :nodoc:
attr_accessor :prefix, :postfix # :nodoc:
attr_accessor :sort_index, :sort_order, :scterm # :nodoc:

Expand Down Expand Up @@ -478,6 +480,8 @@ def find_color_type_and_value(string)
# @param [Array<String>] values
# @param [Integer] optimal_widths
def chunk_values(values, optimal_widths)
return values.map { |value| [value] } unless word_wrap

# First split long strings into an array of chunks, where each chunk size is the calculated column width
values_as_chunks = values.each_with_index.map do |value, idx|
color_state = {}
Expand Down Expand Up @@ -586,6 +590,8 @@ def calculate_optimal_widths(styled_columns, styled_rows)
end
end

return display_width_metadata.map { |metadata| metadata[:max_display_width] } unless word_wrap

# Calculate the sizes set by the user
user_influenced_column_widths = colprops.map do |colprop|
if colprop['Width']
Expand Down Expand Up @@ -647,7 +653,8 @@ def calculate_optimal_widths(styled_columns, styled_rows)
end

def format_table_field(str, idx)
str_cp = str.to_s.encode('UTF-8', invalid: :replace, undef: :replace).strip
str_cp = str.to_s.encode('UTF-8', invalid: :replace, undef: :replace)
str_cp = str_cp.strip if colprops[idx]['Strip']

colprops[idx]['Formatters'].each do |f|
str_cp = f.format(str_cp)
Expand Down
6 changes: 3 additions & 3 deletions spec/rex/text/table_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ def style(str)
expect(described_class.wrap_table?(default_table_options)).to be true
end

it "returns the user preference when set to true" do
it "ignores the user preference when set to true" do
expect(described_class.wrap_table?(enable_wrapped_table_options)).to be true
end

it "returns the user preference when set to false" do
expect(described_class.wrap_table?(disable_wrapped_table_options)).to be false
it "ignores the user preference when set to false" do
expect(described_class.wrap_table?(disable_wrapped_table_options)).to be true
end
end

Expand Down
121 changes: 120 additions & 1 deletion spec/rex/text/wrapped_table_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ def style(str)
allow(Rex::Text::Table).to receive(:wrap_table?).with(anything).and_return(true)
end

before(:each) do
described_class.wrap_tables!
end

after(:each) do
described_class.unwrap_tables!
end

describe "#to_csv" do
it "handles strings in different encodings" do
options = {
Expand Down Expand Up @@ -221,6 +229,82 @@ def style(str)
TABLE
end

context 'when values have trailing and preceding whitespae' do
it 'strips values by default' do
whitespace = " "
col_1_field = whitespace + "A" * 5 + whitespace
col_2_field = whitespace + "B" * 50 + whitespace
col_3_field = whitespace + "C" * 15 + whitespace

options = {
'Header' => 'Header',
'Columns' => [
'Column 1',
'Column 2',
'Column 3'
]
}

tbl = Rex::Text::Table.new(options)

tbl << [
col_1_field,
col_2_field,
col_3_field
]

expect(tbl).to match_table <<~TABLE
Header
======
Column 1 Column 2 Column 3
-------- -------- --------
AAAAA BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCC
TABLE
end

it 'allows conditional stripping of values' do
whitespace = " "
col_1_field = whitespace + "A" * 5 + whitespace
col_2_field = whitespace + "B" * 50 + whitespace
col_3_field = whitespace + "C" * 15 + whitespace

options = {
'Header' => 'Header',
'Columns' => [
'Column 1',
'Column 2',
'Column 3'
],
'ColProps' => {
'Column 1' => {
'Strip' => true
},
'Column 2' => {
'Strip' => false
}
}
}

tbl = Rex::Text::Table.new(options)

tbl << [
col_1_field,
col_2_field,
col_3_field
]

expect(tbl).to match_table <<~TABLE
Header
======
Column 1 Column 2 Column 3
-------- -------- --------
AAAAA BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB CCCCCCCCCCCCCCC
TABLE
end
end

context 'when using arrow indicators' do
let(:empty_column_header_styler) do
clazz = Class.new do
Expand Down Expand Up @@ -442,6 +526,41 @@ def style(str)
TABLE
end

it 'allows wordwrapping to be disabled globally' do
options = {
'Header' => 'Header',
'Width' => 3,
'WordWrap' => false,
'Columns' => [
'Column 1',
'Column 2',
'Column 3'
],
'ColProps' => {
'Column 2' => {
'Stylers' => [styler]
}
}
}

tbl = Rex::Text::Table.new(options)

tbl << [
"A" * 5,
"ABC ABCD ABC" * 1,
"C" * 5
]

expect(tbl).to match_table <<~TABLE
Header
======
Column 1 Column 2 Column 3
-------- -------- --------
AAAAA %bluABC ABCD ABC%clr CCCCC
TABLE
end

it 'should apply field stylers correctly and NOT increase column length when having a low width value' do
options = {
'Header' => 'Header',
Expand Down Expand Up @@ -773,7 +892,7 @@ def style(str)
expect(tbl.to_s.lines).to all(have_maximum_display_width(80))
end

it "Wraps columns as well as values" do
it "wraps columns as well as values" do
options = {
'Header' => 'Header',
'Indent' => 2,
Expand Down

0 comments on commit 1ef344b

Please sign in to comment.