Skip to content

Commit 65a39f5

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

17 files changed

+208
-77
lines changed

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

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: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,20 @@ internal class FormattableFormatter : IValueFormatter
77
public int Priority => 2;
88

99
public bool TryFormat(object? value, string format, out string result)
10+
=> TryFormat(value, format, CultureInfo.InvariantCulture, out result);
11+
12+
public bool TryFormat(object? value, string format, CultureInfo cultureInfo, out string result)
1013
{
1114
result = string.Empty;
1215

1316
if (string.IsNullOrWhiteSpace(format))
1417
return false;
1518

16-
if (IsBlockedFormat(format))
17-
{
18-
result = $"Format '{format}' is not supported in {nameof(FormattableFormatter)}";
19-
return false;
20-
}
21-
2219
if (value is IFormattable formattable)
2320
{
2421
try
2522
{
26-
result = formattable.ToString(format, CultureInfo.InvariantCulture);
23+
result = formattable.ToString(format, cultureInfo);
2724
return true;
2825
}
2926
catch (FormatException)
@@ -35,9 +32,4 @@ public bool TryFormat(object? value, string format, out string result)
3532

3633
return false;
3734
}
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);
4335
}

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
{

src/GitVersion.Core/Formatting/IValueFormatter.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1+
using System.Globalization;
2+
13
namespace GitVersion.Formatting;
24

35
internal interface IValueFormatter
46
{
57
bool TryFormat(object? value, string format, out string result);
68

9+
bool TryFormat(object? value, string format, CultureInfo cultureInfo, out string result);
10+
711
/// <summary>
812
/// Lower number = higher priority
913
/// </summary>

0 commit comments

Comments
 (0)