Skip to content

Commit 9351573

Browse files
committed
Drop TableStyle in favor of ITableRenderer (thanks DennieBee 😘)
1 parent feb7454 commit 9351573

12 files changed

Lines changed: 132 additions & 66 deletions

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -363,19 +363,22 @@ Founder Facebook | Mark Zuckerberg | 1,300,000.00 | 1984-03-14 | |
363363

364364
The `DefaultObjectHandler`, by default, pads all rows with missing values with `null` values.
365365

366-
## Styles
366+
## Table Renderers
367367

368-
The TextTableBuilder has support for (very simple) styles. These can be specified as an optional argument to the `Build()` method. Currently only a very few styles are supported.
368+
The TextTableBuilder uses an `ITableRenderer` to do the actual 'rendering' of the table. The TableRenderer is provided with `RenderColums`, which provide column information, and an `IEnumerable<string[]>` which represents the rows and values. The values have been formatted at this point; the table renderer takes care of aligning, padding etc.
369+
370+
By default, the TextTableBuilder uses the `DefaultTableRenderer` which produced the above examples. Two other, very simple, renderers are provided. These are the `MinimalTableRenderer` and `MSDOSTableRenderer`.
371+
372+
To use a specific `ITableRenderer` you pass one to the `Build()` method:
369373

370-
To specify a style, invoke the `Build()` method with a `style` argument:
371374

372375
```c#
373-
Console.WriteLine(tablebuilder.Build(table, TableStyle.MSDOS));
376+
Console.WriteLine(tablebuilder.Build(table, new MSDOSTableRenderer()));
374377
```
375378

376-
Going back to our [very first example](#quickstart), the following styles are currently implemented. More _may_ be added in the future (as well as ANSI color support etc.):
379+
Going back to our [very first example](#quickstart), the following styles are currently provided. More _may_ be added in the future (as well as ANSI color support etc.) but it's also trivial to build your own; just implement `ITableRenderer`:
377380

378-
### TableStyle.Default
381+
### DefaultTableRenderer
379382

380383
```cmd
381384
No | Name | Position | Salary
@@ -386,7 +389,7 @@ No | Name | Position | Salary
386389
4 | Mark Zuckerberg | Founder Facebook | 1,300,000
387390
```
388391

389-
### TableStyle.Minimal
392+
### MinimalTableRenderer
390393

391394
```cmd
392395
No Name Position Salary
@@ -396,7 +399,7 @@ No Name Position Salary
396399
4 Mark Zuckerberg Founder Facebook 1,300,000
397400
```
398401

399-
### TableStyle.MSDOS
402+
### MSDOSTableRenderer
400403

401404
```cmd
402405
No║Name ║Position ║ Salary

TextTableBuilder/Column.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ namespace TextTableBuilder;
44

55
public record Column(
66
string Name,
7-
Align Align = Column.DefaultAlign,
8-
Align RowAlign = Row.DefaultAlign,
7+
Align HeaderAlign = Column.DefaultAlign,
8+
Align ValueAlign = Row.DefaultAlign,
99
int? MinWidth = null,
1010
int? Width = null,
1111
ITypeHandler? TypeHandler = null
@@ -50,4 +50,4 @@ private static Align GetAlignFromChar(char align)
5050
'~' => Align.Center,
5151
_ => Align.Left
5252
};
53-
}
53+
}

TextTableBuilder/RenderColumn.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
namespace TextTableBuilder;
2+
3+
public record RenderColumn(string Name, int Width, Align HeaderAlign, Align ValueAlign);

TextTableBuilder/TableBuilder.cs

Lines changed: 18 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Globalization;
2-
using System.Text;
32
using TextTableBuilder.ObjectHandlers;
3+
using TextTableBuilder.TableRenderers;
44
using TextTableBuilder.TypeHandlers;
55

66
namespace TextTableBuilder;
@@ -10,21 +10,29 @@ public class TableBuilder
1010
public TypeHandlerCollection TypeHandlers { get; } = new();
1111
public ObjectHandlerCollection ObjectHandlers { get; } = new();
1212

13+
public static readonly ITableRenderer DefaultTableRenderer = new DefaultTableRenderer();
14+
1315
public string Build(Table table)
14-
=> Build(table, TableStyle.Default);
16+
=> Build(table, DefaultTableRenderer, CultureInfo.CurrentUICulture);
17+
18+
public string Build(Table table, ITableRenderer tableRenderer)
19+
=> Build(table, tableRenderer, CultureInfo.CurrentUICulture);
1520

16-
public string Build(Table table, TableStyle tableStyle)
17-
=> Build(table, tableStyle, CultureInfo.CurrentUICulture);
1821
public string Build(Table table, IFormatProvider formatProvider)
19-
=> Build(table, TableStyle.Default, formatProvider);
22+
=> Build(table, DefaultTableRenderer, formatProvider);
2023

21-
public string Build(Table table, TableStyle tableStyle, IFormatProvider formatProvider)
24+
public string Build(Table table, ITableRenderer tableRenderer, IFormatProvider formatProvider)
2225
{
2326
if (table is null)
2427
{
2528
throw new ArgumentNullException(nameof(table));
2629
}
2730

31+
if (tableRenderer is null)
32+
{
33+
throw new ArgumentNullException(nameof(tableRenderer));
34+
}
35+
2836
if (formatProvider is null)
2937
{
3038
throw new ArgumentNullException(nameof(formatProvider));
@@ -66,44 +74,9 @@ public string Build(Table table, TableStyle tableStyle, IFormatProvider formatPr
6674
colwidths = cols.Select((w, i) => Math.Min(w.Width ?? int.MaxValue, colwidths[i])).ToArray();
6775

6876
// Now build actual table
69-
var sb = new StringBuilder();
70-
71-
// Headers
72-
sb.AppendLine(MakeRow(tableStyle, table.Columns.Select((col, i) => MakeCell(col.Name, col.Align, colwidths[i], tableStyle.Padding))));
73-
74-
// Header separator
75-
if (tableStyle.HeaderSeparator is not null)
76-
{
77-
sb.AppendLine(MakeRow(tableStyle, colwidths.Select(w => new string(tableStyle.HeaderSeparator.Value, w))));
78-
}
79-
80-
// Rows
81-
foreach (var row in rows)
82-
{
83-
sb.AppendLine(MakeRow(tableStyle, row.Select((value, i) => MakeCell(value, cols[i].RowAlign, colwidths[i], tableStyle.Padding))));
84-
}
85-
return sb.ToString();
77+
return tableRenderer.Render(
78+
Array.AsReadOnly(table.Columns.Select((c, i) => new RenderColumn(c.Name, colwidths[i], c.HeaderAlign, c.ValueAlign)).ToArray()),
79+
rows
80+
);
8681
}
87-
88-
private static string MakeRow(TableStyle tableStyle, IEnumerable<string> values)
89-
=> string.Join(tableStyle.ColumnSeparator, values);
90-
91-
private static string MakeCell(string value, Align align, int width, char paddingChar)
92-
=> AlignString(TruncateString(value, align, width), align, width, paddingChar);
93-
94-
private static string AlignString(string value, Align align, int width, char paddingChar)
95-
=> align switch
96-
{
97-
Align.Right => value.PadLeft(width, paddingChar),
98-
Align.Center => value.PadLeft((width - value.Length) / 2 + value.Length, paddingChar).PadRight(width, paddingChar),
99-
_ => value.PadRight(width, paddingChar)
100-
};
101-
102-
private static string TruncateString(string value, Align align, int length)
103-
=> align switch
104-
{
105-
Align.Right => value.Substring(Math.Max(value.Length - length, 0), Math.Min(value.Length, length)),
106-
Align.Center => value.Substring(Math.Max((value.Length - length) / 2, 0), Math.Min(length, value.Length)),
107-
_ => value.Substring(0, Math.Min(length, value.Length))
108-
};
10982
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.ObjectModel;
2+
3+
namespace TextTableBuilder.TableRenderers;
4+
5+
public abstract class BaseTableRenderer : ITableRenderer
6+
{
7+
public abstract string Render(ReadOnlyCollection<RenderColumn> columns, IEnumerable<string[]> rows);
8+
9+
protected static string RenderRow(string columnSeparator, IEnumerable<string> values)
10+
=> string.Join(columnSeparator, values);
11+
12+
protected static string RenderCell(string value, Align align, int width, char paddingChar)
13+
=> AlignString(TruncateString(value, align, width), align, width, paddingChar);
14+
15+
protected static string AlignString(string value, Align align, int width, char paddingChar)
16+
=> align switch
17+
{
18+
Align.Right => value.PadLeft(width, paddingChar),
19+
Align.Center => value.PadLeft((width - value.Length) / 2 + value.Length, paddingChar).PadRight(width, paddingChar),
20+
_ => value.PadRight(width, paddingChar)
21+
};
22+
23+
protected static string TruncateString(string value, Align align, int length)
24+
=> align switch
25+
{
26+
Align.Right => value.Substring(Math.Max(value.Length - length, 0), Math.Min(value.Length, length)),
27+
Align.Center => value.Substring(Math.Max((value.Length - length) / 2, 0), Math.Min(length, value.Length)),
28+
_ => value.Substring(0, Math.Min(length, value.Length))
29+
};
30+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace TextTableBuilder.TableRenderers;
2+
3+
public class DefaultTableRenderer : SimpleTableRenderer
4+
{
5+
public DefaultTableRenderer() : base(' ', " | ", '-') { }
6+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
using System.Collections.ObjectModel;
2+
3+
namespace TextTableBuilder.TableRenderers;
4+
5+
public interface ITableRenderer
6+
{
7+
string Render(ReadOnlyCollection<RenderColumn> columns, IEnumerable<string[]> rows);
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace TextTableBuilder.TableRenderers;
2+
3+
public class MSDOSTableRenderer : SimpleTableRenderer
4+
{
5+
public MSDOSTableRenderer() : base(' ', "║", '═') { }
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace TextTableBuilder.TableRenderers;
2+
3+
public class MinimalTableRenderer : SimpleTableRenderer
4+
{
5+
public MinimalTableRenderer() : base(' ', " ", null) { }
6+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System.Collections.ObjectModel;
2+
using System.Text;
3+
4+
namespace TextTableBuilder.TableRenderers;
5+
6+
public abstract class SimpleTableRenderer : BaseTableRenderer
7+
{
8+
private readonly char _paddingchar;
9+
private readonly string _columnseparator;
10+
private readonly char? _rowseparator;
11+
12+
public SimpleTableRenderer(char paddingChar, string columnSeparator, char? rowSeparator)
13+
{
14+
_paddingchar = paddingChar;
15+
_columnseparator = columnSeparator;
16+
_rowseparator = rowSeparator;
17+
}
18+
19+
public override string Render(ReadOnlyCollection<RenderColumn> columns, IEnumerable<string[]> rows)
20+
{
21+
var sb = new StringBuilder();
22+
23+
// Headers
24+
sb.AppendLine(RenderRow(_columnseparator, columns.Select(c => RenderCell(c.Name, c.HeaderAlign, c.Width, _paddingchar))));
25+
26+
// Header separator
27+
if (_rowseparator is not null)
28+
{
29+
sb.AppendLine(RenderRow(_columnseparator, columns.Select(c => new string(_rowseparator.Value, c.Width))));
30+
}
31+
32+
// Rows
33+
foreach (var row in rows)
34+
{
35+
sb.AppendLine(RenderRow(_columnseparator, row.Select((value, i) => RenderCell(value, columns[i].ValueAlign, columns[i].Width, _paddingchar))));
36+
}
37+
return sb.ToString();
38+
}
39+
}

0 commit comments

Comments
 (0)