Skip to content

Commit 646a634

Browse files
committed
Address PR comments II. Apply SOLI>>D<< to ValueFormatter.
1 parent 74cbb00 commit 646a634

18 files changed

+215
-79
lines changed

new-cli/GitVersion.Common/GitVersion.Common.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<Compile Include="..\..\src\GitVersion.Core\Extensions\DictionaryExtensions.cs" Link="%(Filename)%(Extension)" />
1111
<Compile Include="..\..\src\GitVersion.Core\Extensions\StringExtensions.cs" Link="Extensions\StringExtensions.cs" />
1212
<Compile Include="..\..\src\GitVersion.Core\Extensions\CommonExtensions.cs" Link="Extensions\CommonExtensions.cs" />
13+
<Compile Include="..\..\src\GitVersion.Core\Formatting\*.cs" Link="Formatting\%(Filename)%(Extension)" />
1314
<Compile Include="..\..\src\GitVersion.Core\Helpers\*.cs" Link="Helpers\%(Filename)%(Extension)" />
1415
<Compile Include="..\..\src\GitVersion.Core\Git\*.cs" Link="Git\%(Filename)%(Extension)" />
1516
<Compile Include="..\..\src\GitVersion.Core\SemVer\*.cs" Link="SemVer\%(Filename)%(Extension)"/>

src/GitVersion.Core.Tests/Formatting/DateFormatterTests.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public void TryFormat_NullValue_ReturnsFalse()
1717
formatted.ShouldBeEmpty();
1818
}
1919

20-
[TestCase("2021-01-01", "date:yyyy-MM-dd", "2021-01-01")]
21-
[TestCase("2021-01-01T12:00:00Z", "date:yyyy-MM-ddTHH:mm:ssZ", "2021-01-01T12:00:00Z")]
20+
[TestCase("2021-01-01", "yyyy-MM-dd", "2021-01-01")]
21+
[TestCase("2021-01-01T12:00:00Z", "yyyy-MM-ddTHH:mm:ssZ", "2021-01-01T12:00:00Z")]
2222
public void TryFormat_ValidDateFormats_ReturnsExpectedResult(string input, string format, string expected)
2323
{
2424
var date = DateTime.Parse(input);
@@ -27,13 +27,4 @@ public void TryFormat_ValidDateFormats_ReturnsExpectedResult(string input, strin
2727
result.ShouldBeTrue();
2828
formatted.ShouldBe(expected);
2929
}
30-
31-
[Test]
32-
public void TryFormat_UnsupportedFormat_ReturnsFalse()
33-
{
34-
var sut = new DateFormatter();
35-
var result = sut.TryFormat(DateTime.Now, "unsupported", out var formatted);
36-
result.ShouldBeFalse();
37-
formatted.ShouldBeEmpty();
38-
}
3930
}

src/GitVersion.Core.Tests/Formatting/FormattableFormatterTests.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using GitVersion.Formatting;
1+
using System.Globalization;
2+
using GitVersion.Formatting;
23

34
namespace GitVersion.Core.Tests.Formatting;
45

@@ -12,13 +13,16 @@ public class FormattableFormatterTests
1213
public void TryFormat_NullValue_ReturnsFalse()
1314
{
1415
var sut = new FormattableFormatter();
15-
var result = sut.TryFormat(null, "G", out var formatted);
16+
var result = sut.TryFormat(null, "G", CultureInfo.InvariantCulture, out var formatted);
1617
result.ShouldBeFalse();
1718
formatted.ShouldBeEmpty();
1819
}
1920

2021
[TestCase(123.456, "F2", "123.46")]
2122
[TestCase(1234.456, "F2", "1234.46")]
23+
[TestCase(123.456, "C", "¤123.46")]
24+
[TestCase(123.456, "P", "12,345.60 %")]
25+
[TestCase(1234567890, "N0", "1,234,567,890")]
2226
public void TryFormat_ValidFormats_ReturnsExpectedResult(object input, string format, string expected)
2327
{
2428
var sut = new FormattableFormatter();
@@ -27,9 +31,6 @@ public void TryFormat_ValidFormats_ReturnsExpectedResult(object input, string fo
2731
formatted.ShouldBe(expected);
2832
}
2933

30-
[TestCase(123.456, "C", "Format 'C' is not supported in FormattableFormatter")]
31-
[TestCase(123.456, "P", "Format 'P' is not supported in FormattableFormatter")]
32-
[TestCase(1234567890, "N0", "Format 'N0' is not supported in FormattableFormatter")]
3334
[TestCase(1234567890, "Z", "Format 'Z' is not supported in FormattableFormatter")]
3435
public void TryFormat_UnsupportedFormat_ReturnsFalse(object input, string format, string expected)
3536
{
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
using System.Globalization;
2+
using GitVersion.Formatting;
3+
4+
namespace GitVersion.Core.Tests.Formatting;
5+
6+
[TestFixture]
7+
public class ValueFormatterTests
8+
{
9+
[Test]
10+
public void TryFormat_NullValue_ReturnsFalse()
11+
{
12+
var result = ValueFormatter.Default.TryFormat(null, "any", out var formatted);
13+
result.ShouldBeFalse();
14+
formatted.ShouldBeEmpty();
15+
}
16+
17+
[Test]
18+
public void TryFormat_String_UsesStringFormatter()
19+
{
20+
var result = ValueFormatter.Default.TryFormat("hello", "u", out var formatted);
21+
result.ShouldBeTrue();
22+
formatted.ShouldBe("HELLO");
23+
}
24+
25+
[Test]
26+
public void TryFormat_Number_UsesNumericFormatter()
27+
{
28+
var result = ValueFormatter.Default.TryFormat(1234.5678, "n", out var formatted);
29+
result.ShouldBeTrue();
30+
formatted.ShouldBe("1,234.57");
31+
}
32+
33+
[Test]
34+
public void TryFormat_Date_UsesDateFormatter()
35+
{
36+
var date = new DateTime(2023, 12, 25);
37+
var result = ValueFormatter.Default.TryFormat(date, "yyyy-MM-dd", out var formatted);
38+
result.ShouldBeTrue();
39+
formatted.ShouldBe("2023-12-25");
40+
}
41+
42+
[Test]
43+
public void TryFormat_FormattableObject_UsesFormattableFormatter()
44+
{
45+
var value = 123.456m;
46+
var result = ValueFormatter.Default.TryFormat(value, "C", out var formatted);
47+
result.ShouldBeTrue();
48+
formatted.ShouldBe("¤123.46");
49+
}
50+
51+
[Test]
52+
public void TryFormat_InvalidFormat_ReturnsFalse()
53+
{
54+
var result = ValueFormatter.Default.TryFormat("test", "invalidformat", out var formatted);
55+
result.ShouldBeFalse();
56+
formatted.ShouldBeEmpty();
57+
}
58+
59+
[Test]
60+
public void RegisterFormatter_AddsNewFormatter()
61+
{
62+
var customFormatter = new TestFormatter { Priority = 0 };
63+
IValueFormatterCombiner sut = new ValueFormatter();
64+
sut.RegisterFormatter(customFormatter);
65+
var result = sut.TryFormat("test", "custom", out var formatted);
66+
result.ShouldBeTrue();
67+
formatted.ShouldBe("CUSTOM:test");
68+
}
69+
70+
[Test]
71+
public void RemoveFormatter_RemovesExistingFormatter()
72+
{
73+
IValueFormatterCombiner sut = new ValueFormatter();
74+
// First verify numeric formatting works
75+
sut.TryFormat(123.45, "n1", out var before);
76+
before.ShouldBe("123.5");
77+
78+
sut.RemoveFormatter<NumericFormatter>();
79+
80+
// Now numeric formatting will still happen, but via the FormattableFormatter
81+
var result = sut.TryFormat(123.45, "n1", out var afterFormatted);
82+
result.ShouldBeTrue();
83+
afterFormatted.ShouldBe("123.5");
84+
85+
sut.RemoveFormatter<FormattableFormatter>();
86+
87+
// Now numeric formatting will now not be handled by any formatter that remains
88+
result = sut.TryFormat(123.45, "n1", out var afterNotFormatted);
89+
result.ShouldBeFalse();
90+
afterNotFormatted.ShouldBeEmpty();
91+
}
92+
93+
[Test]
94+
public void Formatters_ExecuteInPriorityOrder()
95+
{
96+
IValueFormatterCombiner sut = new ValueFormatter();
97+
var highPriorityFormatter = new TestFormatter { Priority = 0 };
98+
var lowPriorityFormatter = new TestFormatter { Priority = 99 };
99+
100+
sut.RegisterFormatter(lowPriorityFormatter);
101+
sut.RegisterFormatter(highPriorityFormatter);
102+
var result = sut.TryFormat("test", "custom", out var formatted);
103+
result.ShouldBeTrue();
104+
105+
// Should use the high priority formatter first
106+
formatted.ShouldBe("CUSTOM:test");
107+
}
108+
109+
private class TestFormatter : IValueFormatter
110+
{
111+
public int Priority { get; init; }
112+
113+
public bool TryFormat(object? value, string format, out string result)
114+
{
115+
if (format == "custom" && value is string str)
116+
{
117+
result = $"CUSTOM:{str}";
118+
return true;
119+
}
120+
121+
result = string.Empty;
122+
return false;
123+
}
124+
125+
public bool TryFormat(object? value, string format, CultureInfo cultureInfo, out string result)
126+
=> TryFormat(value, format, out result);
127+
}
128+
}

src/GitVersion.Core/Extensions/StringExtensions.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Diagnostics.CodeAnalysis;
2+
using System.Globalization;
23
using GitVersion.Core;
34

45
namespace GitVersion.Extensions;
@@ -31,7 +32,7 @@ public static bool IsEquivalentTo(this string self, string? other) =>
3132
public static string WithPrefixIfNotNullOrEmpty(this string value, string prefix)
3233
=> string.IsNullOrEmpty(value) ? value : prefix + value;
3334

34-
internal static string PascalCase(this string input)
35+
internal static string PascalCase(this string input, CultureInfo cultureInfo)
3536
{
3637
var sb = new StringBuilder(input.Length);
3738
var capitalizeNext = true;
@@ -44,7 +45,7 @@ internal static string PascalCase(this string input)
4445
continue;
4546
}
4647

47-
sb.Append(capitalizeNext ? char.ToUpperInvariant(c) : char.ToLowerInvariant(c));
48+
sb.Append(capitalizeNext ? cultureInfo.TextInfo.ToUpper(c) : cultureInfo.TextInfo.ToLower(c));
4849
capitalizeNext = false;
4950
}
5051

src/GitVersion.Core/Formatting/DateFormatter.cs

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,26 @@
22

33
namespace GitVersion.Formatting;
44

5-
internal class DateFormatter : IValueFormatter
5+
internal class DateFormatter : InvariantFormatter, IValueFormatter
66
{
77
public int Priority => 2;
88

9-
public bool TryFormat(object? value, string format, out string result)
9+
public override bool TryFormat(object? value, string format, CultureInfo cultureInfo, out string result)
1010
{
1111
result = string.Empty;
1212

13-
if (value is DateTime dt && format.StartsWith("date:"))
13+
if (value is DateTime dt)
1414
{
15-
var dateFormat = RemoveDatePrefix(format);
16-
result = dt.ToString(dateFormat, CultureInfo.InvariantCulture);
15+
result = dt.ToString(format, cultureInfo);
1716
return true;
1817
}
1918

20-
if (value is string dateStr && DateTime.TryParse(dateStr, out var parsedDate) && format.StartsWith("date:"))
19+
if (value is string dateStr && DateTime.TryParse(dateStr, out var parsedDate))
2120
{
22-
var dateFormat = format.Substring(5);
23-
result = parsedDate.ToString(dateFormat, CultureInfo.InvariantCulture);
21+
result = parsedDate.ToString(format, cultureInfo);
2422
return true;
2523
}
2624

2725
return false;
2826
}
29-
30-
private static string RemoveDatePrefix(string format) => format.Substring(5);
3127
}

src/GitVersion.Core/Helpers/ExpressionCompiler.cs renamed to src/GitVersion.Core/Formatting/ExpressionCompiler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using System.Linq.Expressions;
22

3-
namespace GitVersion.Helpers;
3+
namespace GitVersion.Formatting;
44

55
internal class ExpressionCompiler : IExpressionCompiler
66
{

src/GitVersion.Core/Formatting/FormattableFormatter.cs

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,22 @@
22

33
namespace GitVersion.Formatting;
44

5-
internal class FormattableFormatter : IValueFormatter
5+
internal class FormattableFormatter : InvariantFormatter, IValueFormatter
66
{
77
public int Priority => 2;
88

9-
public bool TryFormat(object? value, string format, out string result)
9+
public override bool TryFormat(object? value, string format, CultureInfo cultureInfo, out string result)
1010
{
1111
result = string.Empty;
1212

1313
if (string.IsNullOrWhiteSpace(format))
1414
return false;
1515

16-
if (IsBlockedFormat(format))
17-
{
18-
result = $"Format '{format}' is not supported in {nameof(FormattableFormatter)}";
19-
return false;
20-
}
21-
2216
if (value is IFormattable formattable)
2317
{
2418
try
2519
{
26-
result = formattable.ToString(format, CultureInfo.InvariantCulture);
20+
result = formattable.ToString(format, cultureInfo);
2721
return true;
2822
}
2923
catch (FormatException)
@@ -35,9 +29,4 @@ public bool TryFormat(object? value, string format, out string result)
3529

3630
return false;
3731
}
38-
39-
private static bool IsBlockedFormat(string format) =>
40-
format.Equals("C", StringComparison.OrdinalIgnoreCase) ||
41-
format.Equals("P", StringComparison.OrdinalIgnoreCase) ||
42-
format.StartsWith("N", StringComparison.OrdinalIgnoreCase);
4332
}

src/GitVersion.Core/Helpers/IExpressionCompiler.cs renamed to src/GitVersion.Core/Formatting/IExpressionCompiler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace GitVersion.Helpers
1+
namespace GitVersion.Formatting
22
{
33
internal interface IExpressionCompiler
44
{

src/GitVersion.Core/Helpers/IMemberResolver.cs renamed to src/GitVersion.Core/Formatting/IMemberResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace GitVersion.Helpers;
1+
namespace GitVersion.Formatting;
22

33
internal interface IMemberResolver
44
{

0 commit comments

Comments
 (0)