Skip to content

Commit 269bdcb

Browse files
authored
Extend IOutputFormatter with AsTable method for table formatting (#662)
1 parent 6419a83 commit 269bdcb

File tree

9 files changed

+351
-13
lines changed

9 files changed

+351
-13
lines changed

Src/BlueDotBrigade.Weevil.Common/IO/HtmlFormatter.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,41 @@ public sealed class HtmlFormatter : IOutputFormatter
1111
public string AsBullet(string message) => $"<li>{message}</li>";
1212
public string AsNumbered(string message) => $"<li>{_numberedItemCounter++}. {message}</li>";
1313
public string AsError(string message) => $"<span style='color:red;'>ERROR: {message}</span>";
14+
15+
public string AsTable(string[] headers, string[][] rows)
16+
{
17+
var lines = new System.Collections.Generic.List<string>();
18+
19+
lines.Add("<table>");
20+
21+
// Header row
22+
lines.Add(" <thead>");
23+
lines.Add(" <tr>");
24+
foreach (var header in headers)
25+
{
26+
lines.Add($" <th>{header}</th>");
27+
}
28+
lines.Add(" </tr>");
29+
lines.Add(" </thead>");
30+
31+
// Data rows
32+
lines.Add(" <tbody>");
33+
foreach (var row in rows)
34+
{
35+
lines.Add(" <tr>");
36+
foreach (var cell in row)
37+
{
38+
lines.Add($" <td>{cell}</td>");
39+
}
40+
lines.Add(" </tr>");
41+
}
42+
lines.Add(" </tbody>");
43+
44+
lines.Add("</table>");
45+
46+
return string.Join(Environment.NewLine, lines);
47+
}
48+
1449
public void ResetNumbering() => _numberedItemCounter = 1;
1550
}
1651
}

Src/BlueDotBrigade.Weevil.Common/IO/IOutputFormatter.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public interface IOutputFormatter
99
string AsBullet(string message);
1010
string AsNumbered(string message);
1111
string AsError(string message);
12+
string AsTable(string[] headers, string[][] rows);
1213
void ResetNumbering();
1314
}
1415
}

Src/BlueDotBrigade.Weevil.Common/IO/MarkdownFormatter.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,31 @@ public sealed class MarkdownFormatter : IOutputFormatter
1111
public string AsBullet(string message) => $"* {message}";
1212
public string AsNumbered(string message) => $"{_numberedItemCounter++}. {message}";
1313
public string AsError(string message) => $"**ERROR**: {message}";
14+
15+
public string AsTable(string[] headers, string[][] rows)
16+
{
17+
var lines = new System.Collections.Generic.List<string>();
18+
19+
// Header row
20+
lines.Add("| " + string.Join(" | ", headers) + " |");
21+
22+
// Separator row
23+
var separators = new string[headers.Length];
24+
for (int i = 0; i < headers.Length; i++)
25+
{
26+
separators[i] = "---";
27+
}
28+
lines.Add("| " + string.Join(" | ", separators) + " |");
29+
30+
// Data rows
31+
foreach (var row in rows)
32+
{
33+
lines.Add("| " + string.Join(" | ", row) + " |");
34+
}
35+
36+
return string.Join(Environment.NewLine, lines);
37+
}
38+
1439
public void ResetNumbering() => _numberedItemCounter = 1;
1540
}
1641
}

Src/BlueDotBrigade.Weevil.Common/IO/PlainTextFormatter.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,23 @@ public sealed class PlainTextFormatter : IOutputFormatter
1111
public string AsBullet(string message) => $"- {message}";
1212
public string AsNumbered(string message) => $"{_numberedItemCounter++}. {message}";
1313
public string AsError(string message) => $"ERROR: {message}";
14+
15+
public string AsTable(string[] headers, string[][] rows)
16+
{
17+
var lines = new System.Collections.Generic.List<string>();
18+
19+
// Header row (tab-delimited)
20+
lines.Add(string.Join("\t", headers));
21+
22+
// Data rows (tab-delimited)
23+
foreach (var row in rows)
24+
{
25+
lines.Add(string.Join("\t", row));
26+
}
27+
28+
return string.Join(Environment.NewLine, lines);
29+
}
30+
1431
public void ResetNumbering() => _numberedItemCounter = 1;
1532
}
1633
}

Src/BlueDotBrigade.Weevil.Gui/Analysis/GraphViewModel.cs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using BlueDotBrigade.Weevil.Filter.Expressions;
1717
using BlueDotBrigade.Weevil.Filter.Expressions.Regular;
1818
using BlueDotBrigade.Weevil.Gui.Input;
19+
using BlueDotBrigade.Weevil.IO;
1920
using LiveChartsCore;
2021
using LiveChartsCore.Defaults;
2122
using LiveChartsCore.Kernel.Sketches;
@@ -941,33 +942,58 @@ private static ObservableCollection<SeriesMetrics> CalculateSeriesMetrics(
941942
/// </summary>
942943
public string SerializeMetrics()
943944
{
945+
return SerializeMetrics(new PlainTextFormatter());
946+
}
947+
948+
/// <summary>
949+
/// Serializes the metrics data using the specified output formatter.
950+
/// </summary>
951+
/// <param name="formatter">The formatter to use for output formatting.</param>
952+
/// <returns>Formatted metrics data as a string.</returns>
953+
public string SerializeMetrics(IOutputFormatter formatter)
954+
{
955+
if (formatter == null)
956+
{
957+
throw new ArgumentNullException(nameof(formatter));
958+
}
959+
944960
if (this.SeriesMetrics == null || !this.SeriesMetrics.Any())
945961
{
946962
return string.Empty;
947963
}
948964

949-
var lines = new List<string>();
950-
951-
// Header row
952-
lines.Add("Series Name\tCount\tMin\tMax\tMean\tMedian\tRange Start\tRange End");
953-
954-
// Data rows
955-
foreach (var metrics in this.SeriesMetrics)
965+
// Prepare headers
966+
var headers = new[]
967+
{
968+
"Series Name",
969+
"Count",
970+
"Min",
971+
"Max",
972+
"Mean",
973+
"Median",
974+
"Range Start",
975+
"Range End"
976+
};
977+
978+
// Prepare data rows
979+
var rows = new string[this.SeriesMetrics.Count][];
980+
for (int i = 0; i < this.SeriesMetrics.Count; i++)
956981
{
957-
var line = string.Join("\t",
982+
var metrics = this.SeriesMetrics[i];
983+
rows[i] = new[]
984+
{
958985
metrics.SeriesName,
959986
metrics.Count.ToString(),
960987
metrics.MinFormatted,
961988
metrics.MaxFormatted,
962989
metrics.MeanFormatted,
963990
metrics.MedianFormatted,
964991
metrics.RangeStartFormatted,
965-
metrics.RangeEndFormatted);
966-
967-
lines.Add(line);
992+
metrics.RangeEndFormatted
993+
};
968994
}
969-
970-
return string.Join(Environment.NewLine, lines);
995+
996+
return formatter.AsTable(headers, rows);
971997
}
972998

973999
/// <summary>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace BlueDotBrigade.Weevil.IO
2+
{
3+
using System;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
6+
[TestClass]
7+
public class HtmlFormatterTests
8+
{
9+
[TestMethod]
10+
public void AsTable_WithValidData_ShouldGenerateHtmlTable()
11+
{
12+
// Arrange
13+
var formatter = new HtmlFormatter();
14+
var headers = new[] { "Name", "Age", "City" };
15+
var rows = new[]
16+
{
17+
new[] { "Alice", "30", "New York" },
18+
new[] { "Bob", "25", "Los Angeles" }
19+
};
20+
21+
// Act
22+
var result = formatter.AsTable(headers, rows);
23+
24+
// Assert
25+
Assert.IsTrue(result.Contains("<table>"));
26+
Assert.IsTrue(result.Contains("<thead>"));
27+
Assert.IsTrue(result.Contains("<tbody>"));
28+
Assert.IsTrue(result.Contains("</table>"));
29+
Assert.IsTrue(result.Contains("<th>Name</th>"));
30+
Assert.IsTrue(result.Contains("<td>Alice</td>"));
31+
}
32+
33+
[TestMethod]
34+
public void AsTable_WithEmptyRows_ShouldReturnTableWithHeaderOnly()
35+
{
36+
// Arrange
37+
var formatter = new HtmlFormatter();
38+
var headers = new[] { "Name", "Age" };
39+
var rows = new string[0][];
40+
41+
// Act
42+
var result = formatter.AsTable(headers, rows);
43+
44+
// Assert
45+
Assert.IsTrue(result.Contains("<table>"));
46+
Assert.IsTrue(result.Contains("<thead>"));
47+
Assert.IsTrue(result.Contains("<tbody>"));
48+
Assert.IsTrue(result.Contains("</table>"));
49+
Assert.IsTrue(result.Contains("<th>Name</th>"));
50+
Assert.IsTrue(result.Contains("<th>Age</th>"));
51+
}
52+
}
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
namespace BlueDotBrigade.Weevil.IO
2+
{
3+
using System;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
6+
[TestClass]
7+
public class MarkdownFormatterTests
8+
{
9+
[TestMethod]
10+
public void AsTable_WithValidData_ShouldGenerateMarkdownTable()
11+
{
12+
// Arrange
13+
var formatter = new MarkdownFormatter();
14+
var headers = new[] { "Name", "Age", "City" };
15+
var rows = new[]
16+
{
17+
new[] { "Alice", "30", "New York" },
18+
new[] { "Bob", "25", "Los Angeles" }
19+
};
20+
21+
// Act
22+
var result = formatter.AsTable(headers, rows);
23+
24+
// Assert
25+
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
26+
Assert.AreEqual(4, lines.Length); // Header + separator + 2 data rows
27+
Assert.AreEqual("| Name | Age | City |", lines[0]);
28+
Assert.AreEqual("| --- | --- | --- |", lines[1]);
29+
Assert.AreEqual("| Alice | 30 | New York |", lines[2]);
30+
Assert.AreEqual("| Bob | 25 | Los Angeles |", lines[3]);
31+
}
32+
33+
[TestMethod]
34+
public void AsTable_WithEmptyRows_ShouldReturnHeaderAndSeparator()
35+
{
36+
// Arrange
37+
var formatter = new MarkdownFormatter();
38+
var headers = new[] { "Name", "Age" };
39+
var rows = new string[0][];
40+
41+
// Act
42+
var result = formatter.AsTable(headers, rows);
43+
44+
// Assert
45+
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
46+
Assert.AreEqual(2, lines.Length); // Header + separator
47+
Assert.AreEqual("| Name | Age |", lines[0]);
48+
Assert.AreEqual("| --- | --- |", lines[1]);
49+
}
50+
}
51+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
namespace BlueDotBrigade.Weevil.IO
2+
{
3+
using System;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
6+
[TestClass]
7+
public class PlainTextFormatterTests
8+
{
9+
[TestMethod]
10+
public void AsTable_WithValidData_ShouldGenerateTabDelimitedTable()
11+
{
12+
// Arrange
13+
var formatter = new PlainTextFormatter();
14+
var headers = new[] { "Name", "Age", "City" };
15+
var rows = new[]
16+
{
17+
new[] { "Alice", "30", "New York" },
18+
new[] { "Bob", "25", "Los Angeles" }
19+
};
20+
21+
// Act
22+
var result = formatter.AsTable(headers, rows);
23+
24+
// Assert
25+
Assert.IsTrue(result.Contains("\t"));
26+
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
27+
Assert.AreEqual(3, lines.Length); // Header + 2 data rows
28+
Assert.AreEqual("Name\tAge\tCity", lines[0]);
29+
Assert.AreEqual("Alice\t30\tNew York", lines[1]);
30+
Assert.AreEqual("Bob\t25\tLos Angeles", lines[2]);
31+
}
32+
33+
[TestMethod]
34+
public void AsTable_WithEmptyRows_ShouldReturnOnlyHeader()
35+
{
36+
// Arrange
37+
var formatter = new PlainTextFormatter();
38+
var headers = new[] { "Name", "Age" };
39+
var rows = new string[0][];
40+
41+
// Act
42+
var result = formatter.AsTable(headers, rows);
43+
44+
// Assert
45+
var lines = result.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
46+
Assert.AreEqual(1, lines.Length);
47+
Assert.AreEqual("Name\tAge", lines[0]);
48+
}
49+
}
50+
}

0 commit comments

Comments
 (0)