Skip to content

Commit e558ef0

Browse files
committed
ADD: Conditional interpolated string handlers
1 parent dec517e commit e558ef0

File tree

6 files changed

+319
-0
lines changed

6 files changed

+319
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### New features
1111

12+
- Structs `Louis.Text.TrueConditionInterpolatedStringHandler` and `Louis.Text.FalseConditionInterpolatedStringHandler` are interpolated string handlers that only perform formatting if a given condition is true or false, respectively.
13+
They can help implement, for example, conditional logging methods that take a condition and an interpolated string as a parameter, ensuring that string interpolation will only be performed if the result is actually used.
14+
1215
### Changes to existing features
1316

1417
### Bugs fixed in this release
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,29 @@
11
#nullable enable
2+
Louis.Text.FalseConditionInterpolatedStringHandler
3+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void
4+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(string? value) -> void
5+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void
6+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value) -> void
7+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) -> void
8+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value) -> void
9+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment) -> void
10+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment, string? format) -> void
11+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, string? format) -> void
12+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendLiteral(string! value) -> void
13+
Louis.Text.FalseConditionInterpolatedStringHandler.FalseConditionInterpolatedStringHandler() -> void
14+
Louis.Text.FalseConditionInterpolatedStringHandler.FalseConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) -> void
15+
Louis.Text.FalseConditionInterpolatedStringHandler.ToStringAndClear() -> string!
16+
Louis.Text.TrueConditionInterpolatedStringHandler
17+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void
18+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(string? value) -> void
19+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void
20+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value) -> void
21+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) -> void
22+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value) -> void
23+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment) -> void
24+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment, string? format) -> void
25+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, string? format) -> void
26+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendLiteral(string! value) -> void
27+
Louis.Text.TrueConditionInterpolatedStringHandler.ToStringAndClear() -> string!
28+
Louis.Text.TrueConditionInterpolatedStringHandler.TrueConditionInterpolatedStringHandler() -> void
29+
Louis.Text.TrueConditionInterpolatedStringHandler.TrueConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) -> void
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,29 @@
11
#nullable enable
2+
Louis.Text.FalseConditionInterpolatedStringHandler
3+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void
4+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(string? value) -> void
5+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void
6+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value) -> void
7+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) -> void
8+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value) -> void
9+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment) -> void
10+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment, string? format) -> void
11+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, string? format) -> void
12+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendLiteral(string! value) -> void
13+
Louis.Text.FalseConditionInterpolatedStringHandler.FalseConditionInterpolatedStringHandler() -> void
14+
Louis.Text.FalseConditionInterpolatedStringHandler.FalseConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) -> void
15+
Louis.Text.FalseConditionInterpolatedStringHandler.ToStringAndClear() -> string!
16+
Louis.Text.TrueConditionInterpolatedStringHandler
17+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void
18+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(string? value) -> void
19+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void
20+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value) -> void
21+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) -> void
22+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value) -> void
23+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment) -> void
24+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment, string? format) -> void
25+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, string? format) -> void
26+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendLiteral(string! value) -> void
27+
Louis.Text.TrueConditionInterpolatedStringHandler.ToStringAndClear() -> string!
28+
Louis.Text.TrueConditionInterpolatedStringHandler.TrueConditionInterpolatedStringHandler() -> void
29+
Louis.Text.TrueConditionInterpolatedStringHandler.TrueConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) -> void
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,29 @@
11
#nullable enable
2+
Louis.Text.FalseConditionInterpolatedStringHandler
3+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void
4+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(string? value) -> void
5+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void
6+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value) -> void
7+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) -> void
8+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value) -> void
9+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment) -> void
10+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment, string? format) -> void
11+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendFormatted<T>(T value, string? format) -> void
12+
Louis.Text.FalseConditionInterpolatedStringHandler.AppendLiteral(string! value) -> void
13+
Louis.Text.FalseConditionInterpolatedStringHandler.FalseConditionInterpolatedStringHandler() -> void
14+
Louis.Text.FalseConditionInterpolatedStringHandler.FalseConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) -> void
15+
Louis.Text.FalseConditionInterpolatedStringHandler.ToStringAndClear() -> string!
16+
Louis.Text.TrueConditionInterpolatedStringHandler
17+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(object? value, int alignment = 0, string? format = null) -> void
18+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(string? value) -> void
19+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(string? value, int alignment = 0, string? format = null) -> void
20+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value) -> void
21+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted(System.ReadOnlySpan<char> value, int alignment = 0, string? format = null) -> void
22+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value) -> void
23+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment) -> void
24+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, int alignment, string? format) -> void
25+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendFormatted<T>(T value, string? format) -> void
26+
Louis.Text.TrueConditionInterpolatedStringHandler.AppendLiteral(string! value) -> void
27+
Louis.Text.TrueConditionInterpolatedStringHandler.ToStringAndClear() -> string!
28+
Louis.Text.TrueConditionInterpolatedStringHandler.TrueConditionInterpolatedStringHandler() -> void
29+
Louis.Text.TrueConditionInterpolatedStringHandler.TrueConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend) -> void
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright (c) Tenacom and contributors. Licensed under the MIT license.
2+
// See the LICENSE file in the project root for full license information.
3+
4+
#if NET6_0_OR_GREATER
5+
6+
using System;
7+
using System.Runtime.CompilerServices;
8+
using System.Text;
9+
10+
namespace Louis.Text;
11+
12+
#pragma warning disable CA1815 // Override equals and operator equals on value types - They would never be used anyway
13+
#pragma warning disable RS0027 // API with optional parameter(s) should have the most parameters amongst its public overloads - Just mimicking StringBuilder.AppendInterpolatedStringHandler's APIs here.
14+
15+
/// <summary>
16+
/// <para>Provides an interpolated string handler that only performs formatting if a given condition is <see langword="false"/>.</para>
17+
/// <para>This type is only available on .NET 6.0 and greater.</para>
18+
/// <para>This type is only meant to be used by compiler-generated code. As such, it performs no parameter validation whatsoever.</para>
19+
/// <example>
20+
/// <para>The following code shows how this type can be used as an interpolated string handler:</para>
21+
/// <code>
22+
/// using System;
23+
/// using System.Runtime.CompilerServices;
24+
///
25+
/// public static class ConsoleUtility
26+
/// {
27+
/// // Write a message to the console only if condition is false.
28+
/// // Usage example: ConsoleUtility.WriteLineUnless(myVar > 0, $"{nameof(myVar)} is {myVar} but should be greater than zero.");
29+
/// // String interpolation will only take place if necessary.
30+
/// public static void WriteLineUnless(bool condition, [InterpolatedStringHandlerArgument(nameof(condition))] ref FalseConditionInterpolatedStringHandler message)
31+
/// {
32+
/// if (!condition)
33+
/// {
34+
/// Console.WriteLine(message.ToStringAndClear());
35+
/// }
36+
/// }
37+
/// }
38+
/// </code>
39+
/// </example>
40+
/// </summary>
41+
[InterpolatedStringHandler]
42+
public struct FalseConditionInterpolatedStringHandler
43+
{
44+
private StringBuilder? _builder;
45+
private StringBuilder.AppendInterpolatedStringHandler _handler;
46+
47+
/// <summary>
48+
/// Initializes a new instance of the <see cref="FalseConditionInterpolatedStringHandler"/> struct.
49+
/// </summary>
50+
/// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
51+
/// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
52+
/// <param name="condition">The condition that determines whether formatting will actually take place.</param>
53+
/// <param name="shouldAppend">A value indicating whether formatting should proceed.</param>
54+
/// <remarks>
55+
/// <para>This constructor is only intended to be called by compiler-generated code.
56+
/// As such, it doesn't perform argument validation.</para>
57+
/// </remarks>
58+
public FalseConditionInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend)
59+
{
60+
shouldAppend = !condition;
61+
if (shouldAppend)
62+
{
63+
var builder = new StringBuilder();
64+
_handler = new(literalLength, formattedCount, builder);
65+
_builder = builder;
66+
}
67+
}
68+
69+
/// <summary>
70+
/// Extracts the built string and frees any allocated memory.
71+
/// </summary>
72+
/// <returns>The built string.</returns>
73+
public string ToStringAndClear()
74+
{
75+
if (_builder is null)
76+
{
77+
return string.Empty;
78+
}
79+
80+
var message = _builder.ToString();
81+
this = default;
82+
return message;
83+
}
84+
85+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendLiteral(string)"/>
86+
public void AppendLiteral(string value) => _handler.AppendLiteral(value);
87+
88+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T)"/>
89+
public void AppendFormatted<T>(T value) => _handler.AppendFormatted(value);
90+
91+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, string?)"/>
92+
public void AppendFormatted<T>(T value, string? format) => _handler.AppendFormatted(value, format);
93+
94+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, int)"/>
95+
public void AppendFormatted<T>(T value, int alignment) => _handler.AppendFormatted(value, alignment);
96+
97+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, int, string?)"/>
98+
public void AppendFormatted<T>(T value, int alignment, string? format) => _handler.AppendFormatted(value, alignment, format);
99+
100+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(ReadOnlySpan{char})"/>
101+
public void AppendFormatted(ReadOnlySpan<char> value) => _handler.AppendFormatted(value);
102+
103+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(ReadOnlySpan{char}, int, string?)"/>
104+
public void AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null) => _handler.AppendFormatted(value, alignment, format);
105+
106+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(string?)"/>
107+
public void AppendFormatted(string? value) => _handler.AppendFormatted(value);
108+
109+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(string?, int, string?)"/>
110+
public void AppendFormatted(string? value, int alignment = 0, string? format = null) => _handler.AppendFormatted(value, alignment, format);
111+
112+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(object?, int, string?)"/>
113+
public void AppendFormatted(object? value, int alignment = 0, string? format = null) => _handler.AppendFormatted(value, alignment, format);
114+
}
115+
116+
#endif

0 commit comments

Comments
 (0)