Skip to content

Commit

Permalink
Add an option to limit how many elements to render for collections an…
Browse files Browse the repository at this point in the history
…d arrays. (#29)
  • Loading branch information
hermanussen authored Aug 28, 2024
1 parent 2392019 commit 7ee9fcd
Show file tree
Hide file tree
Showing 14 changed files with 459 additions and 20 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,14 @@ new Dictionary<string, string>
```
![image](https://user-images.githubusercontent.com/8770486/232251913-add4a0d8-3355-44f6-ba94-5dfbf8d8e2ac.png)

You can ensure that arrays, dictionaries and collections don't output too much by allowing results to be truncated. Do this by setting the `MaxCollectionCount` property in the tableConfig.

```csharp
int[] arr = [1, 2, 3, 4];

// Outputs only the first two elements and a message that says: ... truncated 2 items
arr.Dump(tableConfig: new () { MaxCollectionCount = 2 });
```

### You can turn on or off fields and private members
```csharp
Expand Down
3 changes: 3 additions & 0 deletions src/Dumpify.Playground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
using System.Data;
using System.Text;

int[] arr = [1, 2, 3, 4];
arr.Dump(tableConfig: new () { MaxCollectionCount = 2 });

//DumpConfig.Default.Renderer = Renderers.Text;
//DumpConfig.Default.ColorConfig = ColorConfig.NoColors;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
using System.Collections;
using System.Data;
using Xunit.Abstractions;

namespace Dumpify.Tests.Renderers.Spectre.Console;
public class MultiValueTruncationTests
{
private readonly ITestOutputHelper _testOutputHelper;

public MultiValueTruncationTests(ITestOutputHelper testOutputHelper)
{
_testOutputHelper = testOutputHelper;
}

[Theory]
[ClassData(typeof(TruncateCollectionsData))]
public void TruncateCollections(string name, object collection, int maxCount, string[] shouldContainItems, string[] shouldNotContainItems)
{
//Arrange
_testOutputHelper.WriteLine($"Test: {name}");

//Act
var result = collection.DumpText(tableConfig: new TableConfig() { MaxCollectionCount = maxCount });

_testOutputHelper.WriteLine(result);

//Assert
foreach(var shouldContain in shouldContainItems)
{
result.Should().Contain(shouldContain);
}

foreach (var shouldNotContain in shouldNotContainItems)
{
result.Should().NotContain(shouldNotContain);
}
}

public class TruncateCollectionsData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[]
{
"Don't truncate if the number of items is equal to the max count",
Enumerable.Range(1, 10).ToArray(),
10,
new [] { "9 │ 10" },
new [] { "... truncated" }
};

yield return new object[]
{
"Don't truncate if the number of items is smaller than the max count",
Enumerable.Range(1, 10).ToArray(),
20,
new [] { "9 │ 10" },
new [] { " ... truncated" }
};

yield return new object[]
{
"Truncate if the number of items is greater than the max count",
Enumerable.Range(1, 10).ToArray(),
5,
new [] { "4 │ 5", " │ ... truncated 5 items" },
new [] { "5 │ 6" }
};

yield return new object[]
{
"Truncate for lists",
Enumerable.Range(1, 10).ToList(),
5,
new [] { "│ 5", "│ ... truncated 5 items" },
new [] { "│ 6" }
};

yield return new object[]
{
"Truncate for data tables",
CreateDataTable(10),
5,
new []
{
"│ 4 │ 8 │ 12 │ 16 │ 20 │ │",
"│ Value1 │ Value2 │ Value3 │ Value4 │ Value5 │ ... +5 │",
"│ ... +5 │ │ │"
},
new [] { "│ 5 │ 10 │ 15 │ 20 │ 25 │ │" }
};

yield return new object[]
{
"Truncate for datasets",
CreateDataSet(4),
2,
new [] { "Table 2", "... truncated 2 more tables" },
new [] { "Table 3" }
};

yield return new object[]
{
"Truncate for dictionaries",
Enumerable.Range(1, 10).ToDictionary(x => x, x => $"value for {x}"),
5,
new []
{
"│ 5 │ \"value for 5\"",
"│ │ ... truncated 5 items │"
},
new [] { "│ 6 │ \"value for 6\"" }
};

yield return new object[]
{
"Truncate for two-dimensional arrays",
new int[6, 6]
{
{ 1, 2, 3, 4, 5, 6 },
{ 1, 2, 3, 4, 5, 6 },
{ 1, 2, 3, 4, 5, 6 },
{ 1, 2, 3, 4, 5, 6 },
{ 1, 2, 3, 4, 5, 6 },
{ 1, 2, 3, 4, 5, 6 }
},
3,
new []
{
"│ 2 │ 1 │ 2 │ 3 │ │",
"│ # │ 0 │ 1 │ 2 │ ... +3 │",
"│ ... +3 │ │ │ │ │"
},
new []
{
"│ 3 │ 1 │ 2 │ 3 │ │"
}
};

yield return new object[]
{
"Truncate for three-dimensional arrays (they get flattened to a single list)",
new int[4, 4, 4]
{
{
{ 1, 2, 3, 4 },
{ 5, 6, 7, 8 },
{ 9, 10, 11, 12 },
{ 13, 14, 15, 16 }
},
{
{ 17, 18, 19, 20 },
{ 21, 22, 23, 24 },
{ 25, 26, 27, 28 },
{ 29, 30, 31, 32 }
},
{
{ 33, 34, 35, 36 },
{ 37, 38, 39, 40 },
{ 41, 42, 43, 44 },
{ 45, 46, 47, 48 }
},
{
{ 49, 50, 51, 52 },
{ 53, 54, 55, 56 },
{ 57, 58, 59, 60 },
{ 61, 62, 63, 64 }
}
},
10,
new []
{
"│ 10",
"│ ... truncated 54 items"
},
new []
{
"│ 11"
}
};
}

private static DataSet CreateDataSet(int tables)
{
DataSet dataSet = new DataSet();
for (int i = 0; i < tables; i++)
{
var dataTable = CreateDataTable(10);
dataTable.TableName = $"Table {i + 1}";
dataSet.Tables.Add(dataTable);
}

return dataSet;
}

private static DataTable CreateDataTable(int rows)
{
var dataTable = new DataTable
{
Columns =
{
new DataColumn("Value1", typeof(int)),
new DataColumn("Value2", typeof(int)),
new DataColumn("Value3", typeof(int)),
new DataColumn("Value4", typeof(int)),
new DataColumn("Value5", typeof(int)),
new DataColumn("Value6", typeof(int)),
new DataColumn("Value7", typeof(int)),
new DataColumn("Value8", typeof(int)),
new DataColumn("Value9", typeof(int)),
new DataColumn("Value10", typeof(int))
}
};

for(int i = 0; i < rows; i++)
{
dataTable.Rows.Add(i * 1, i * 2, i * 3, i * 4, i * 5, i * 6, i * 7, i * 8, i * 9, i * 10);
}

return dataTable;
}

IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
1 change: 1 addition & 0 deletions src/Dumpify/Config/TableConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public class TableConfig
public bool ExpandTables { get; set; } = false;
public bool ShowMemberTypes { get; set; } = false;
public bool ShowRowSeparators { get; set; } = false;
public int MaxCollectionCount { get; set; } = int.MaxValue;
}
7 changes: 7 additions & 0 deletions src/Dumpify/Descriptors/LabelDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Dumpify.Descriptors.ValueProviders;

namespace Dumpify.Descriptors;
internal record LabelDescriptor(Type Type, IValueProvider? ValueProvider) : IDescriptor
{
public string Name => ValueProvider?.Name ?? Type.Name;
}
3 changes: 2 additions & 1 deletion src/Dumpify/Renderers/RendererBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public TRenderable RenderDescriptor(object? @object, IDescriptor? descriptor, Re
SingleValueDescriptor singleDescriptor => TryRenderCustomTypeDescriptor(@object, singleDescriptor, context, RenderSingleValueDescriptor),
ObjectDescriptor objDescriptor => TryRenderCustomTypeDescriptor(@object, objDescriptor, context, TryRenderObjectDescriptor),
MultiValueDescriptor multiDescriptor => TryRenderCustomTypeDescriptor(@object, multiDescriptor, context, RenderMultiValueDescriptor),
LabelDescriptor labelDescriptor => TryRenderCustomTypeDescriptor(@object, labelDescriptor, context, RenderLabelDescriptor),
CustomDescriptor customDescriptor => TryRenderCustomTypeDescriptor(@object, customDescriptor, context, RenderCustomDescriptor),
_ => RenderUnsupportedDescriptor(@object, descriptor, context),
};
Expand Down Expand Up @@ -170,6 +171,6 @@ protected virtual (bool success, object? value, TRenderable renderedValue) GetVa
protected abstract TRenderable RenderUnsupportedDescriptor(object obj, IDescriptor descriptor, RenderContext<TState> context);
protected abstract TRenderable RenderObjectDescriptor(object obj, ObjectDescriptor descriptor, RenderContext<TState> context);
protected abstract TRenderable RenderMultiValueDescriptor(object obj, MultiValueDescriptor descriptor, RenderContext<TState> context);

protected abstract TRenderable RenderLabelDescriptor(object obj, LabelDescriptor descriptor, RenderContext<TState> context);
protected abstract TState CreateState(object? obj, IDescriptor? descriptor, RendererConfig config);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Dumpify;
public class RowIndicesTableBuilderBehavior : ITableBuilderBehavior
{
private readonly string _indexColumnName = Markup.Escape("#");
private readonly Dictionary<int, string?> _rowIndexOverrides = new();

public IEnumerable<IRenderable> GetAdditionalCells(object? obj, IDescriptor? currentDescriptor, RenderContext<SpectreRendererState> context)
{
Expand All @@ -21,7 +22,17 @@ public IEnumerable<IRenderable> GetAdditionalColumns(RenderContext<SpectreRender
public IEnumerable<IRenderable> GetAdditionalRowElements(BehaviorContext behaviorContext, RenderContext<SpectreRendererState> context)
{
var index = behaviorContext.AddedRows;
if(_rowIndexOverrides.ContainsKey(index))
{
yield return new Markup(Markup.Escape(_rowIndexOverrides[index] ?? string.Empty), new Style(foreground: context.State.Colors.ColumnNameColor));
yield break;
}

yield return new Markup(Markup.Escape(index.ToString()), new Style(foreground: context.State.Colors.ColumnNameColor));
}

public void AddHideIndexForRow(int row, string? value = null)
{
_rowIndexOverrides.Add(row, value);
}
}
Loading

0 comments on commit 7ee9fcd

Please sign in to comment.