Skip to content

Commit 967006b

Browse files
committed
Restore Summary block
1 parent 2c89d14 commit 967006b

File tree

1 file changed

+82
-60
lines changed

1 file changed

+82
-60
lines changed

src/GitVersion.Core/Helpers/StringFormatWith.cs

Lines changed: 82 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -5,78 +5,100 @@ namespace GitVersion.Helpers;
55

66
internal static class StringFormatWithExtension
77
{
8-
internal static IExpressionCompiler ExpressionCompiler { get; set; } = new ExpressionCompiler();
8+
internal static IExpressionCompiler ExpressionCompiler { get; set; } = new ExpressionCompiler();
9+
10+
internal static IInputSanitizer InputSanitizer { get; set; } = new InputSanitizer();
11+
12+
internal static IMemberResolver MemberResolver { get; set; } = new MemberResolver();
13+
14+
/// <summary>
15+
/// Formats the <paramref name="template"/>, replacing each expression wrapped in curly braces
16+
/// with the corresponding property from the <paramref name="source"/> or <paramref name="environment"/>.
17+
/// </summary>
18+
/// <param name="template" this="true">The source template, which may contain expressions to be replaced, e.g '{Foo.Bar.CurrencySymbol} foo {Foo.Bar.Price}'</param>
19+
/// <param name="source">The source object to apply to the <paramref name="template"/></param>
20+
/// <param name="environment"></param>
21+
/// <exception cref="ArgumentNullException">The <paramref name="template"/> is null.</exception>
22+
/// <exception cref="ArgumentException">An environment variable was null and no fallback was provided.</exception>
23+
/// <remarks>
24+
/// An expression containing "." is treated as a property or field access on the <paramref name="source"/>.
25+
/// An expression starting with "env:" is replaced with the value of the corresponding variable from the <paramref name="environment"/>.
26+
/// Each expression may specify a single hardcoded fallback value using the {Prop ?? "fallback"} syntax, which applies if the expression evaluates to null.
27+
/// </remarks>
28+
/// <example>
29+
/// // replace an expression with a property value
30+
/// "Hello {Name}".FormatWith(new { Name = "Fred" }, env);
31+
/// "Hello {Name ?? \"Fred\"}".FormatWith(new { Name = GetNameOrNull() }, env);
32+
/// // replace an expression with an environment variable
33+
/// "{env:BUILD_NUMBER}".FormatWith(new { }, env);
34+
/// "{env:BUILD_NUMBER ?? \"0\"}".FormatWith(new { }, env);
35+
/// </example>
36+
public static string FormatWith<T>(this string template, T? source, IEnvironment environment)
37+
{
38+
ArgumentNullException.ThrowIfNull(template);
39+
ArgumentNullException.ThrowIfNull(source);
40+
41+
var result = new StringBuilder();
42+
var lastIndex = 0;
43+
44+
foreach (var match in RegexPatterns.Common.ExpandTokensRegex().Matches(template).Cast<Match>())
45+
{
46+
var replacement = EvaluateMatch(match, source, environment);
47+
result.Append(template, lastIndex, match.Index - lastIndex);
48+
result.Append(replacement);
49+
lastIndex = match.Index + match.Length;
50+
}
951

10-
internal static IInputSanitizer InputSanitizer { get; set; } = new InputSanitizer();
52+
result.Append(template, lastIndex, template.Length - lastIndex);
53+
return result.ToString();
54+
}
1155

12-
internal static IMemberResolver MemberResolver { get; set; } = new MemberResolver();
56+
private static string EvaluateMatch<T>(Match match, T source, IEnvironment environment)
57+
{
58+
var fallback = match.Groups["fallback"].Success ? match.Groups["fallback"].Value : null;
1359

14-
public static string FormatWith<T>(this string template, T? source, IEnvironment environment)
60+
if (match.Groups["envvar"].Success)
1561
{
16-
ArgumentNullException.ThrowIfNull(template);
17-
ArgumentNullException.ThrowIfNull(source);
18-
19-
var result = new StringBuilder();
20-
var lastIndex = 0;
21-
22-
foreach (var match in RegexPatterns.Common.ExpandTokensRegex().Matches(template).Cast<Match>())
23-
{
24-
var replacement = EvaluateMatch(match, source, environment);
25-
result.Append(template, lastIndex, match.Index - lastIndex);
26-
result.Append(replacement);
27-
lastIndex = match.Index + match.Length;
28-
}
29-
30-
result.Append(template, lastIndex, template.Length - lastIndex);
31-
return result.ToString();
62+
return EvaluateEnvVar(match.Groups["envvar"].Value, fallback, environment);
3263
}
3364

34-
private static string EvaluateMatch<T>(Match match, T source, IEnvironment environment)
65+
if (match.Groups["member"].Success)
3566
{
36-
var fallback = match.Groups["fallback"].Success ? match.Groups["fallback"].Value : null;
37-
38-
if (match.Groups["envvar"].Success)
39-
{
40-
return EvaluateEnvVar(match.Groups["envvar"].Value, fallback, environment);
41-
}
42-
43-
if (match.Groups["member"].Success)
44-
{
45-
var format = match.Groups["format"].Success ? match.Groups["format"].Value : null;
46-
return EvaluateMember(source, match.Groups["member"].Value, format, fallback);
47-
}
48-
49-
throw new ArgumentException($"Invalid token format: '{match.Value}'");
67+
var format = match.Groups["format"].Success ? match.Groups["format"].Value : null;
68+
return EvaluateMember(source, match.Groups["member"].Value, format, fallback);
5069
}
5170

52-
private static string EvaluateEnvVar(string name, string? fallback, IEnvironment env)
71+
throw new ArgumentException($"Invalid token format: '{match.Value}'");
72+
}
73+
74+
private static string EvaluateEnvVar(string name, string? fallback, IEnvironment env)
75+
{
76+
var safeName = InputSanitizer.SanitizeEnvVarName(name);
77+
return env.GetEnvironmentVariable(safeName)
78+
?? fallback
79+
?? throw new ArgumentException($"Environment variable {safeName} not found and no fallback provided");
80+
}
81+
82+
private static string EvaluateMember<T>(T source, string member, string? format, string? fallback)
83+
{
84+
var safeMember = InputSanitizer.SanitizeMemberName(member);
85+
var memberPath = MemberResolver.ResolveMemberPath(source!.GetType(), safeMember);
86+
var getter = ExpressionCompiler.CompileGetter(source.GetType(), memberPath);
87+
var value = getter(source);
88+
89+
if (value is null)
5390
{
54-
var safeName = InputSanitizer.SanitizeEnvVarName(name);
55-
return env.GetEnvironmentVariable(safeName)
56-
?? fallback
57-
?? throw new ArgumentException($"Environment variable {safeName} not found and no fallback provided");
91+
return fallback ?? string.Empty;
5892
}
5993

60-
private static string EvaluateMember<T>(T source, string member, string? format, string? fallback)
94+
if (format is not null && ValueFormatter.TryFormat(
95+
value,
96+
InputSanitizer.SanitizeFormat(format),
97+
out var formatted))
6198
{
62-
var safeMember = InputSanitizer.SanitizeMemberName(member);
63-
var memberPath = MemberResolver.ResolveMemberPath(source!.GetType(), safeMember);
64-
var getter = ExpressionCompiler.CompileGetter(source.GetType(), memberPath);
65-
var value = getter(source);
66-
67-
if (value is null)
68-
{
69-
return fallback ?? string.Empty;
70-
}
71-
72-
if (format is not null && ValueFormatter.TryFormat(
73-
value,
74-
InputSanitizer.SanitizeFormat(format),
75-
out var formatted))
76-
{
77-
return formatted;
78-
}
79-
80-
return value.ToString() ?? fallback ?? string.Empty;
99+
return formatted;
81100
}
101+
102+
return value.ToString() ?? fallback ?? string.Empty;
103+
}
82104
}

0 commit comments

Comments
 (0)