Skip to content

Commit 978f162

Browse files
committed
feat: More overloads for AppendFormat
1 parent 9c467d5 commit 978f162

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ All notable changes to **ValueStringBuilder** will be documented in this file. T
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Two more overloads for `AppendFormat` for up to 5 generic arguments
12+
913
## [1.11.5] - 2023-01-09
1014

1115
### Added

src/LinkDotNet.StringBuilder/ValueStringBuilder.AppendFormat.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,168 @@ public void AppendFormat<T1, T2, T3>(
197197
}
198198
}
199199

200+
/// <summary>
201+
/// Appends the format string to the given <see cref="ValueStringBuilder"/> instance.
202+
/// </summary>
203+
/// <param name="format">Format string.</param>
204+
/// <param name="arg1">Argument for <c>{0}</c>.</param>
205+
/// <param name="arg2">Argument for <c>{1}</c>.</param>
206+
/// <param name="arg3">Argument for <c>{2}</c>.</param>
207+
/// <param name="arg4">Argument for <c>{3}</c>.</param>
208+
/// <typeparam name="T1">Any type for <param name="arg1"></param>.</typeparam>
209+
/// <typeparam name="T2">Any type for <param name="arg2"></param>.</typeparam>
210+
/// <typeparam name="T3">Any type for <param name="arg3"></param>.</typeparam>
211+
/// <typeparam name="T4">Any type for <param name="arg4"></param>.</typeparam>
212+
/// <remarks>
213+
/// The current version does not allow for a custom format.
214+
/// So: <code>AppendFormat("{0:00}")</code> is not allowed and will result in an exception.
215+
/// </remarks>
216+
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
217+
public void AppendFormat<T1, T2, T3, T4>(
218+
[StringSyntax(StringSyntaxAttribute.CompositeFormat)] ReadOnlySpan<char> format,
219+
T1 arg1,
220+
T2 arg2,
221+
T3 arg3,
222+
T4 arg4)
223+
{
224+
var formatIndex = 0;
225+
var start = 0;
226+
while (formatIndex < format.Length)
227+
{
228+
var c = format[formatIndex];
229+
if (c == '{')
230+
{
231+
var endIndex = format[(formatIndex + 1)..].IndexOf('}');
232+
if (endIndex == -1)
233+
{
234+
Append(format);
235+
return;
236+
}
237+
238+
if (start != formatIndex)
239+
{
240+
Append(format[start..formatIndex]);
241+
}
242+
243+
var placeholder = format.Slice(formatIndex, endIndex + 2);
244+
245+
var index = GetValidArgumentIndex(placeholder, 3);
246+
247+
switch (index)
248+
{
249+
case 0:
250+
AppendInternal(arg1);
251+
break;
252+
case 1:
253+
AppendInternal(arg2);
254+
break;
255+
case 2:
256+
AppendInternal(arg3);
257+
break;
258+
case 3:
259+
AppendInternal(arg4);
260+
break;
261+
}
262+
263+
formatIndex += endIndex + 2;
264+
start = formatIndex;
265+
}
266+
else
267+
{
268+
formatIndex++;
269+
}
270+
}
271+
272+
if (start != formatIndex)
273+
{
274+
Append(format[start..formatIndex]);
275+
}
276+
}
277+
278+
/// <summary>
279+
/// Appends the format string to the given <see cref="ValueStringBuilder"/> instance.
280+
/// </summary>
281+
/// <param name="format">Format string.</param>
282+
/// <param name="arg1">Argument for <c>{0}</c>.</param>
283+
/// <param name="arg2">Argument for <c>{1}</c>.</param>
284+
/// <param name="arg3">Argument for <c>{2}</c>.</param>
285+
/// <param name="arg4">Argument for <c>{3}</c>.</param>
286+
/// <param name="arg5">Argument for <c>{4}</c>.</param>
287+
/// <typeparam name="T1">Any type for <param name="arg1"></param>.</typeparam>
288+
/// <typeparam name="T2">Any type for <param name="arg2"></param>.</typeparam>
289+
/// <typeparam name="T3">Any type for <param name="arg3"></param>.</typeparam>
290+
/// <typeparam name="T4">Any type for <param name="arg4"></param>.</typeparam>
291+
/// <typeparam name="T5">Any type for <param name="arg5"></param>.</typeparam>
292+
/// <remarks>
293+
/// The current version does not allow for a custom format.
294+
/// So: <code>AppendFormat("{0:00}")</code> is not allowed and will result in an exception.
295+
/// </remarks>
296+
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
297+
public void AppendFormat<T1, T2, T3, T4, T5>(
298+
[StringSyntax(StringSyntaxAttribute.CompositeFormat)] ReadOnlySpan<char> format,
299+
T1 arg1,
300+
T2 arg2,
301+
T3 arg3,
302+
T4 arg4,
303+
T5 arg5)
304+
{
305+
var formatIndex = 0;
306+
var start = 0;
307+
while (formatIndex < format.Length)
308+
{
309+
var c = format[formatIndex];
310+
if (c == '{')
311+
{
312+
var endIndex = format[(formatIndex + 1)..].IndexOf('}');
313+
if (endIndex == -1)
314+
{
315+
Append(format);
316+
return;
317+
}
318+
319+
if (start != formatIndex)
320+
{
321+
Append(format[start..formatIndex]);
322+
}
323+
324+
var placeholder = format.Slice(formatIndex, endIndex + 2);
325+
326+
var index = GetValidArgumentIndex(placeholder, 4);
327+
328+
switch (index)
329+
{
330+
case 0:
331+
AppendInternal(arg1);
332+
break;
333+
case 1:
334+
AppendInternal(arg2);
335+
break;
336+
case 2:
337+
AppendInternal(arg3);
338+
break;
339+
case 3:
340+
AppendInternal(arg4);
341+
break;
342+
case 4:
343+
AppendInternal(arg5);
344+
break;
345+
}
346+
347+
formatIndex += endIndex + 2;
348+
start = formatIndex;
349+
}
350+
else
351+
{
352+
formatIndex++;
353+
}
354+
}
355+
356+
if (start != formatIndex)
357+
{
358+
Append(format[start..formatIndex]);
359+
}
360+
}
361+
200362
[MethodImpl(MethodImplOptions.AggressiveInlining)]
201363
private static int GetValidArgumentIndex(ReadOnlySpan<char> placeholder, int allowedRange)
202364
{

tests/LinkDotNet.StringBuilder.UnitTests/ValueStringBuilder.AppendFormat.Tests.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,32 @@ public void ShouldAppendFormatWithThreeArguments(string format, object arg1, obj
6666

6767
builder.ToString().Should().Be(expected);
6868
}
69+
70+
[Theory]
71+
[InlineData("Hello {0} {1} {2} {3}", 2, 3, 4, 5, "Hello 2 3 4 5")]
72+
[InlineData("{0}{0}{1}{2}{3}", 2, 3, 3, 2, "22332")]
73+
[InlineData("{0} World", "Hello", "", "", "", "Hello World")]
74+
[InlineData("Hello World", "2", "", "", "", "Hello World")]
75+
public void ShouldAppendFormatWithFourArguments(string format, object arg1, object arg2, object arg3, object arg4, string expected)
76+
{
77+
using var builder = new ValueStringBuilder();
78+
79+
builder.AppendFormat(format, arg1, arg2, arg3, arg4);
80+
81+
builder.ToString().Should().Be(expected);
82+
}
83+
84+
[Theory]
85+
[InlineData("Hello {0} {1} {2} {3} {4}", 2, 3, 4, 5, 3, "Hello 2 3 4 5 3")]
86+
[InlineData("{0}{0}{1}{2}{3}{4}", 2, 3, 3, 2, 2, "223322")]
87+
[InlineData("{0} World", "Hello", "", "", "", "", "Hello World")]
88+
[InlineData("Hello World", "2", "", "", "", "", "Hello World")]
89+
public void ShouldAppendFormatWithFiveArguments(string format, object arg1, object arg2, object arg3, object arg4, object arg5, string expected)
90+
{
91+
using var builder = new ValueStringBuilder();
92+
93+
builder.AppendFormat(format, arg1, arg2, arg3, arg4, arg5);
94+
95+
builder.ToString().Should().Be(expected);
96+
}
6997
}

0 commit comments

Comments
 (0)