Skip to content

Commit 88f2a07

Browse files
authored
Merge pull request #70 from CommunityToolkit/dev/guard-assert-interpolated-handlers
Add interpolated handler overload for Guard.IsTrue/IsFalse
2 parents 9136501 + e9344a6 commit 88f2a07

File tree

3 files changed

+413
-70
lines changed

3 files changed

+413
-70
lines changed
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System;
6+
using System.ComponentModel;
7+
using System.Diagnostics.CodeAnalysis;
8+
using System.Runtime.CompilerServices;
9+
using System.Text;
10+
11+
namespace CommunityToolkit.Diagnostics;
12+
13+
/// <inheritdoc/>
14+
public static partial class Guard
15+
{
16+
/// <summary>
17+
/// Asserts that the input value must be <see langword="true"/>.
18+
/// </summary>
19+
/// <param name="value">The input <see cref="bool"/> to test.</param>
20+
/// <param name="name">The name of the input parameter being tested.</param>
21+
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="false"/>.</exception>
22+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
23+
public static void IsTrue([DoesNotReturnIf(false)] bool value, [CallerArgumentExpression("value")] string name = "")
24+
{
25+
if (value)
26+
{
27+
return;
28+
}
29+
30+
ThrowHelper.ThrowArgumentExceptionForIsTrue(name);
31+
}
32+
33+
/// <summary>
34+
/// Asserts that the input value must be <see langword="true"/>.
35+
/// </summary>
36+
/// <param name="value">The input <see cref="bool"/> to test.</param>
37+
/// <param name="name">The name of the input parameter being tested.</param>
38+
/// <param name="message">A message to display if <paramref name="value"/> is <see langword="false"/>.</param>
39+
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="false"/>.</exception>
40+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
41+
public static void IsTrue([DoesNotReturnIf(false)] bool value, string name, string message)
42+
{
43+
if (value)
44+
{
45+
return;
46+
}
47+
48+
ThrowHelper.ThrowArgumentExceptionForIsTrue(name, message);
49+
}
50+
51+
/// <summary>
52+
/// Asserts that the input value must be <see langword="false"/>.
53+
/// </summary>
54+
/// <param name="value">The input <see cref="bool"/> to test.</param>
55+
/// <param name="name">The name of the input parameter being tested.</param>
56+
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="true"/>.</exception>
57+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
58+
public static void IsFalse([DoesNotReturnIf(true)] bool value, [CallerArgumentExpression("value")] string name = "")
59+
{
60+
if (!value)
61+
{
62+
return;
63+
}
64+
65+
ThrowHelper.ThrowArgumentExceptionForIsFalse(name);
66+
}
67+
68+
/// <summary>
69+
/// Asserts that the input value must be <see langword="false"/>.
70+
/// </summary>
71+
/// <param name="value">The input <see cref="bool"/> to test.</param>
72+
/// <param name="name">The name of the input parameter being tested.</param>
73+
/// <param name="message">A message to display if <paramref name="value"/> is <see langword="true"/>.</param>
74+
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="true"/>.</exception>
75+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
76+
public static void IsFalse([DoesNotReturnIf(true)] bool value, string name, string message)
77+
{
78+
if (!value)
79+
{
80+
return;
81+
}
82+
83+
ThrowHelper.ThrowArgumentExceptionForIsFalse(name, message);
84+
}
85+
86+
#if NET6_0_OR_GREATER
87+
/// <summary>
88+
/// Asserts that the input value must be <see langword="true"/>.
89+
/// </summary>
90+
/// <param name="value">The input <see cref="bool"/> to test.</param>
91+
/// <param name="name">The name of the input parameter being tested.</param>
92+
/// <param name="message">A message to display if <paramref name="value"/> is <see langword="false"/>.</param>
93+
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="false"/>.</exception>
94+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
95+
public static void IsTrue([DoesNotReturnIf(false)] bool value, string name, [InterpolatedStringHandlerArgument("value")] ref IsTrueInterpolatedStringHandler message)
96+
{
97+
if (value)
98+
{
99+
return;
100+
}
101+
102+
ThrowHelper.ThrowArgumentExceptionForIsTrue(name, message.ToStringAndClear());
103+
}
104+
105+
/// <summary>
106+
/// Asserts that the input value must be <see langword="false"/>.
107+
/// </summary>
108+
/// <param name="value">The input <see cref="bool"/> to test.</param>
109+
/// <param name="name">The name of the input parameter being tested.</param>
110+
/// <param name="message">A message to display if <paramref name="value"/> is <see langword="true"/>.</param>
111+
/// <exception cref="ArgumentException">Thrown if <paramref name="value"/> is <see langword="true"/>.</exception>
112+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
113+
public static void IsFalse([DoesNotReturnIf(true)] bool value, string name, [InterpolatedStringHandlerArgument("value")] ref IsFalseInterpolatedStringHandler message)
114+
{
115+
if (!value)
116+
{
117+
return;
118+
}
119+
120+
ThrowHelper.ThrowArgumentExceptionForIsFalse(name, message.ToStringAndClear());
121+
}
122+
123+
/// <summary>
124+
/// Provides an interpolated string handler for <see cref="IsTrue(bool, string, ref IsTrueInterpolatedStringHandler)"/> that only performs formatting if the assert fails.
125+
/// </summary>
126+
[EditorBrowsable(EditorBrowsableState.Never)]
127+
[InterpolatedStringHandler]
128+
public struct IsTrueInterpolatedStringHandler
129+
{
130+
/// <summary>
131+
/// The handler used to perform the formatting.
132+
/// </summary>
133+
private StringBuilder.AppendInterpolatedStringHandler handler;
134+
135+
/// <summary>
136+
/// The underlying <see cref="StringBuilder"/> instance used by <see cref="handler"/>, if any.
137+
/// </summary>
138+
private StringBuilder? builder;
139+
140+
/// <summary>
141+
/// Creates an instance of the handler.
142+
/// </summary>
143+
/// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
144+
/// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
145+
/// <param name="condition">The condition passed to the <see cref="Guard"/> method.</param>
146+
/// <param name="shouldAppend">A value indicating whether formatting should proceed.</param>
147+
/// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
148+
public IsTrueInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend)
149+
{
150+
if (condition)
151+
{
152+
this.handler = default;
153+
this.builder = null;
154+
155+
shouldAppend = false;
156+
}
157+
else
158+
{
159+
StringBuilder builder = new();
160+
161+
this.handler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder);
162+
this.builder = builder;
163+
164+
shouldAppend = true;
165+
}
166+
}
167+
168+
/// <summary>
169+
/// Extracts the built string from the handler.
170+
/// </summary>
171+
internal string ToStringAndClear()
172+
{
173+
string message = this.builder?.ToString() ?? string.Empty;
174+
175+
this = default;
176+
177+
return message;
178+
}
179+
180+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendLiteral(string)"/>
181+
public void AppendLiteral(string value)
182+
{
183+
this.handler.AppendLiteral(value);
184+
}
185+
186+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T)"/>
187+
public void AppendFormatted<T>(T value)
188+
{
189+
this.handler.AppendFormatted(value);
190+
}
191+
192+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, string?)"/>
193+
public void AppendFormatted<T>(T value, string? format)
194+
{
195+
this.handler.AppendFormatted(value, format);
196+
}
197+
198+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, int)"/>
199+
public void AppendFormatted<T>(T value, int alignment)
200+
{
201+
this.handler.AppendFormatted(value, alignment);
202+
}
203+
204+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, int, string?)"/>
205+
public void AppendFormatted<T>(T value, int alignment, string? format)
206+
{
207+
this.handler.AppendFormatted(value, alignment, format);
208+
}
209+
210+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(ReadOnlySpan{char})"/>
211+
public void AppendFormatted(ReadOnlySpan<char> value)
212+
{
213+
this.handler.AppendFormatted(value);
214+
}
215+
216+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(ReadOnlySpan{char}, int, string?)"/>
217+
public void AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null)
218+
{
219+
this.handler.AppendFormatted(value, alignment, format);
220+
}
221+
222+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(string?)"/>
223+
public void AppendFormatted(string? value)
224+
{
225+
this.handler.AppendFormatted(value);
226+
}
227+
228+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(string?, int, string?)"/>
229+
public void AppendFormatted(string? value, int alignment = 0, string? format = null)
230+
{
231+
this.handler.AppendFormatted(value, alignment, format);
232+
}
233+
234+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(object?, int, string?)"/>
235+
public void AppendFormatted(object? value, int alignment = 0, string? format = null)
236+
{
237+
this.handler.AppendFormatted(value, alignment, format);
238+
}
239+
}
240+
241+
/// <summary>
242+
/// Provides an interpolated string handler for <see cref="IsFalse(bool, string, ref IsFalseInterpolatedStringHandler)"/> that only performs formatting if the assert fails.
243+
/// </summary>
244+
[EditorBrowsable(EditorBrowsableState.Never)]
245+
[InterpolatedStringHandler]
246+
public struct IsFalseInterpolatedStringHandler
247+
{
248+
/// <summary>
249+
/// The handler used to perform the formatting.
250+
/// </summary>
251+
private StringBuilder.AppendInterpolatedStringHandler handler;
252+
253+
/// <summary>
254+
/// The underlying <see cref="StringBuilder"/> instance used by <see cref="handler"/>, if any.
255+
/// </summary>
256+
private StringBuilder? builder;
257+
258+
/// <summary>
259+
/// Creates an instance of the handler.
260+
/// </summary>
261+
/// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
262+
/// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
263+
/// <param name="condition">The condition passed to the <see cref="Guard"/> method.</param>
264+
/// <param name="shouldAppend">A value indicating whether formatting should proceed.</param>
265+
/// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
266+
public IsFalseInterpolatedStringHandler(int literalLength, int formattedCount, bool condition, out bool shouldAppend)
267+
{
268+
if (!condition)
269+
{
270+
this.handler = default;
271+
this.builder = null;
272+
273+
shouldAppend = false;
274+
}
275+
else
276+
{
277+
StringBuilder builder = new();
278+
279+
this.handler = new StringBuilder.AppendInterpolatedStringHandler(literalLength, formattedCount, builder);
280+
this.builder = builder;
281+
282+
shouldAppend = true;
283+
}
284+
}
285+
286+
/// <summary>
287+
/// Extracts the built string from the handler.
288+
/// </summary>
289+
internal string ToStringAndClear()
290+
{
291+
string message = this.builder?.ToString() ?? string.Empty;
292+
293+
this = default;
294+
295+
return message;
296+
}
297+
298+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendLiteral(string)"/>
299+
public void AppendLiteral(string value)
300+
{
301+
this.handler.AppendLiteral(value);
302+
}
303+
304+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T)"/>
305+
public void AppendFormatted<T>(T value)
306+
{
307+
this.handler.AppendFormatted(value);
308+
}
309+
310+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, string?)"/>
311+
public void AppendFormatted<T>(T value, string? format)
312+
{
313+
this.handler.AppendFormatted(value, format);
314+
}
315+
316+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, int)"/>
317+
public void AppendFormatted<T>(T value, int alignment)
318+
{
319+
this.handler.AppendFormatted(value, alignment);
320+
}
321+
322+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted{T}(T, int, string?)"/>
323+
public void AppendFormatted<T>(T value, int alignment, string? format)
324+
{
325+
this.handler.AppendFormatted(value, alignment, format);
326+
}
327+
328+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(ReadOnlySpan{char})"/>
329+
public void AppendFormatted(ReadOnlySpan<char> value)
330+
{
331+
this.handler.AppendFormatted(value);
332+
}
333+
334+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(ReadOnlySpan{char}, int, string?)"/>
335+
public void AppendFormatted(ReadOnlySpan<char> value, int alignment = 0, string? format = null)
336+
{
337+
this.handler.AppendFormatted(value, alignment, format);
338+
}
339+
340+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(string?)"/>
341+
public void AppendFormatted(string? value)
342+
{
343+
this.handler.AppendFormatted(value);
344+
}
345+
346+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(string?, int, string?)"/>
347+
public void AppendFormatted(string? value, int alignment = 0, string? format = null)
348+
{
349+
this.handler.AppendFormatted(value, alignment, format);
350+
}
351+
352+
/// <inheritdoc cref="StringBuilder.AppendInterpolatedStringHandler.AppendFormatted(object?, int, string?)"/>
353+
public void AppendFormatted(object? value, int alignment = 0, string? format = null)
354+
{
355+
this.handler.AppendFormatted(value, alignment, format);
356+
}
357+
}
358+
#endif
359+
}

0 commit comments

Comments
 (0)