1
+ using System . Diagnostics . CodeAnalysis ;
2
+
3
+ namespace LinkDotNet . StringBuilder ;
4
+
5
+ public ref partial struct ValueStringBuilder
6
+ {
7
+ /// <summary>
8
+ /// Appends the format string to the given <see cref="ValueStringBuilder"/> instance.
9
+ /// </summary>
10
+ /// <param name="format">Format string.</param>
11
+ /// <param name="arg">Argument for <c>{0}</c>.</param>
12
+ /// <typeparam name="T">Any type.</typeparam>
13
+ /// <remarks>
14
+ /// The current version does not allow for a custom format.
15
+ /// So: <code>AppendFormat("{0:00}")</code> is not allowed and will result in an exception.
16
+ /// </remarks>
17
+ public void AppendFormat < T > (
18
+ [ StringSyntax ( StringSyntaxAttribute . CompositeFormat ) ] ReadOnlySpan < char > format ,
19
+ T arg )
20
+ {
21
+ var formatIndex = 0 ;
22
+ while ( formatIndex < format . Length )
23
+ {
24
+ var c = format [ formatIndex ] ;
25
+ if ( c == '{' )
26
+ {
27
+ var endIndex = format [ ( formatIndex + 1 ) ..] . IndexOf ( '}' ) ;
28
+ if ( endIndex == - 1 )
29
+ {
30
+ Append ( format ) ;
31
+ return ;
32
+ }
33
+
34
+ var placeholder = format . Slice ( formatIndex , endIndex + 2 ) ;
35
+
36
+ GetValidArgumentIndex ( placeholder , 0 ) ;
37
+
38
+ AppendInternal ( arg ) ;
39
+ formatIndex += endIndex + 2 ;
40
+ }
41
+ else
42
+ {
43
+ Append ( c ) ;
44
+ formatIndex ++ ;
45
+ }
46
+ }
47
+ }
48
+
49
+ /// <summary>
50
+ /// Appends the format string to the given <see cref="ValueStringBuilder"/> instance.
51
+ /// </summary>
52
+ /// <param name="format">Format string.</param>
53
+ /// <param name="arg1">Argument for <c>{0}</c>.</param>
54
+ /// <param name="arg2">Argument for <c>{1}</c>.</param>
55
+ /// <typeparam name="T1">Any type for <param name="arg1"></param>.</typeparam>
56
+ /// <typeparam name="T2">Any type for <param name="arg2"></param>.</typeparam>
57
+ /// <remarks>
58
+ /// The current version does not allow for a custom format.
59
+ /// So: <code>AppendFormat("{0:00}")</code> is not allowed and will result in an exception.
60
+ /// </remarks>
61
+ public void AppendFormat < T1 , T2 > (
62
+ [ StringSyntax ( StringSyntaxAttribute . CompositeFormat ) ] ReadOnlySpan < char > format ,
63
+ T1 arg1 ,
64
+ T2 arg2 )
65
+ {
66
+ var formatIndex = 0 ;
67
+ while ( formatIndex < format . Length )
68
+ {
69
+ var c = format [ formatIndex ] ;
70
+ if ( c == '{' )
71
+ {
72
+ var endIndex = format [ ( formatIndex + 1 ) ..] . IndexOf ( '}' ) ;
73
+ if ( endIndex == - 1 )
74
+ {
75
+ Append ( format ) ;
76
+ return ;
77
+ }
78
+
79
+ var placeholder = format . Slice ( formatIndex , endIndex + 2 ) ;
80
+
81
+ var index = GetValidArgumentIndex ( placeholder , 1 ) ;
82
+
83
+ switch ( index )
84
+ {
85
+ case 0 :
86
+ AppendInternal ( arg1 ) ;
87
+ break ;
88
+ case 1 :
89
+ AppendInternal ( arg2 ) ;
90
+ break ;
91
+ }
92
+
93
+ formatIndex += endIndex + 2 ;
94
+ }
95
+ else
96
+ {
97
+ Append ( c ) ;
98
+ formatIndex ++ ;
99
+ }
100
+ }
101
+ }
102
+
103
+ /// <summary>
104
+ /// Appends the format string to the given <see cref="ValueStringBuilder"/> instance.
105
+ /// </summary>
106
+ /// <param name="format">Format string.</param>
107
+ /// <param name="arg1">Argument for <c>{0}</c>.</param>
108
+ /// <param name="arg2">Argument for <c>{1}</c>.</param>
109
+ /// <param name="arg3">Argument for <c>{2}</c>.</param>
110
+ /// <typeparam name="T1">Any type for <param name="arg1"></param>.</typeparam>
111
+ /// <typeparam name="T2">Any type for <param name="arg2"></param>.</typeparam>
112
+ /// <typeparam name="T3">Any type for <param name="arg3"></param>.</typeparam>
113
+ /// <remarks>
114
+ /// The current version does not allow for a custom format.
115
+ /// So: <code>AppendFormat("{0:00}")</code> is not allowed and will result in an exception.
116
+ /// </remarks>
117
+ public void AppendFormat < T1 , T2 , T3 > (
118
+ [ StringSyntax ( StringSyntaxAttribute . CompositeFormat ) ] ReadOnlySpan < char > format ,
119
+ T1 arg1 ,
120
+ T2 arg2 ,
121
+ T3 arg3 )
122
+ {
123
+ var formatIndex = 0 ;
124
+ while ( formatIndex < format . Length )
125
+ {
126
+ var c = format [ formatIndex ] ;
127
+ if ( c == '{' )
128
+ {
129
+ var endIndex = format [ ( formatIndex + 1 ) ..] . IndexOf ( '}' ) ;
130
+ if ( endIndex == - 1 )
131
+ {
132
+ Append ( format ) ;
133
+ return ;
134
+ }
135
+
136
+ var placeholder = format . Slice ( formatIndex , endIndex + 2 ) ;
137
+
138
+ var index = GetValidArgumentIndex ( placeholder , 2 ) ;
139
+
140
+ switch ( index )
141
+ {
142
+ case 0 :
143
+ AppendInternal ( arg1 ) ;
144
+ break ;
145
+ case 1 :
146
+ AppendInternal ( arg2 ) ;
147
+ break ;
148
+ case 2 :
149
+ AppendInternal ( arg3 ) ;
150
+ break ;
151
+ }
152
+
153
+ formatIndex += endIndex + 2 ;
154
+ }
155
+ else
156
+ {
157
+ Append ( c ) ;
158
+ formatIndex ++ ;
159
+ }
160
+ }
161
+ }
162
+
163
+ private static int GetValidArgumentIndex ( ReadOnlySpan < char > placeholder , int allowedRange )
164
+ {
165
+ #pragma warning disable MA0011
166
+ if ( ! int . TryParse ( placeholder [ 1 ..^ 1 ] , out var argIndex ) )
167
+ #pragma warning restore MA0011
168
+ {
169
+ throw new FormatException ( "Invalid argument index in format string: " + placeholder . ToString ( ) ) ;
170
+ }
171
+
172
+ if ( argIndex < 0 || argIndex > allowedRange )
173
+ {
174
+ throw new FormatException ( "Invalid argument index in format string: " + placeholder . ToString ( ) ) ;
175
+ }
176
+
177
+ return argIndex ;
178
+ }
179
+ }
0 commit comments