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