@@ -6,23 +6,46 @@ namespace Cucumber.HtmlFormatter;
6
6
public class MessagesToHtmlWriter : IDisposable
7
7
{
8
8
private StreamWriter writer ;
9
+ private Func < StreamWriter , Envelope , Task > asyncStreamSerializer ;
9
10
private Action < StreamWriter , Envelope > streamSerializer ;
10
11
private string template ;
11
12
private JsonInHtmlWriter JsonInHtmlWriter ;
12
13
private bool streamClosed = false ;
13
14
private bool preMessageWritten = false ;
14
15
private bool firstMessageWritten = false ;
15
16
private bool postMessageWritten = false ;
17
+ private bool isAsyncInitialized = false ;
16
18
19
+ [ Obsolete ( "Cucumber.HtmlFormatter moving to async only operations. Please use the MessagesToHtmlWriter(Stream, Func<StreamWriter, Envelope, Task>) constructor" , false ) ]
17
20
public MessagesToHtmlWriter ( Stream stream , Action < StreamWriter , Envelope > streamSerializer ) : this ( new StreamWriter ( stream ) , streamSerializer )
18
21
{
19
22
}
23
+ public MessagesToHtmlWriter ( Stream stream , Func < StreamWriter , Envelope , Task > asyncStreamSerializer ) : this ( new StreamWriter ( stream ) , asyncStreamSerializer ) { }
24
+
25
+ [ Obsolete ( "Cucumber.HtmlFormatter moving to async only operations. Please use the MessagesToHtmlWriter(StreamWriter, Func<StreamWriter, Envelope, Task>) constructor" , false ) ]
20
26
public MessagesToHtmlWriter ( StreamWriter writer , Action < StreamWriter , Envelope > streamSerializer )
21
27
{
22
28
this . writer = writer ;
23
29
this . streamSerializer = streamSerializer ;
30
+ // Create async wrapper for sync serializer
31
+ this . asyncStreamSerializer = ( w , e ) =>
32
+ {
33
+ streamSerializer ( w , e ) ;
34
+ return Task . CompletedTask ;
35
+ } ;
24
36
template = GetResource ( "index.mustache.html" ) ;
25
37
JsonInHtmlWriter = new JsonInHtmlWriter ( writer ) ;
38
+ isAsyncInitialized = false ;
39
+ }
40
+ public MessagesToHtmlWriter ( StreamWriter writer , Func < StreamWriter , Envelope , Task > asyncStreamSerializer )
41
+ {
42
+ this . writer = writer ;
43
+ this . asyncStreamSerializer = asyncStreamSerializer ;
44
+ // Create sync wrapper for async serializer (will block)
45
+ this . streamSerializer = ( w , e ) => asyncStreamSerializer ( w , e ) . GetAwaiter ( ) . GetResult ( ) ;
46
+ template = GetResource ( "index.mustache.html" ) ;
47
+ JsonInHtmlWriter = new JsonInHtmlWriter ( writer ) ;
48
+ isAsyncInitialized = true ;
26
49
}
27
50
28
51
private void WritePreMessage ( )
@@ -32,15 +55,35 @@ private void WritePreMessage()
32
55
WriteTemplateBetween ( writer , template , "{{css}}" , "{{messages}}" ) ;
33
56
}
34
57
58
+ private async Task WritePreMessageAsync ( )
59
+ {
60
+ await WriteTemplateBetweenAsync ( writer , template , null , "{{css}}" ) ;
61
+ await WriteResourceAsync ( writer , "main.css" ) ;
62
+ await WriteTemplateBetweenAsync ( writer , template , "{{css}}" , "{{messages}}" ) ;
63
+ }
64
+
35
65
private void WritePostMessage ( )
36
66
{
37
67
WriteTemplateBetween ( writer , template , "{{messages}}" , "{{script}}" ) ;
38
68
WriteResource ( writer , "main.js" ) ;
39
69
WriteTemplateBetween ( writer , template , "{{script}}" , null ) ;
40
70
}
41
71
72
+ private async Task WritePostMessageAsync ( )
73
+ {
74
+ await WriteTemplateBetweenAsync ( writer , template , "{{messages}}" , "{{script}}" ) ;
75
+ await WriteResourceAsync ( writer , "main.js" ) ;
76
+ await WriteTemplateBetweenAsync ( writer , template , "{{script}}" , null ) ;
77
+ }
78
+
42
79
public void Write ( Envelope envelope )
43
80
{
81
+ if ( isAsyncInitialized )
82
+ {
83
+ // Log a warning or use other diagnostics
84
+ System . Diagnostics . Debug . WriteLine ( "Warning: Using synchronous Write when initialized with async serializer" ) ;
85
+ }
86
+
44
87
if ( streamClosed ) { throw new IOException ( "Stream closed" ) ; }
45
88
46
89
if ( ! preMessageWritten )
@@ -62,6 +105,37 @@ public void Write(Envelope envelope)
62
105
streamSerializer ( JsonInHtmlWriter , envelope ) ;
63
106
JsonInHtmlWriter . Flush ( ) ;
64
107
}
108
+ public async Task WriteAsync ( Envelope envelope )
109
+ {
110
+ if ( ! isAsyncInitialized )
111
+ {
112
+ // Log a warning or use other diagnostics
113
+ System . Diagnostics . Debug . WriteLine ( "Warning: Using asynchronous WriteAsync when initialized with sync serializer" ) ;
114
+ }
115
+
116
+ if ( streamClosed ) { throw new IOException ( "Stream closed" ) ; }
117
+
118
+ if ( ! preMessageWritten )
119
+ {
120
+ await WritePreMessageAsync ( ) ;
121
+ preMessageWritten = true ;
122
+ await writer . FlushAsync ( ) ;
123
+ }
124
+ if ( ! firstMessageWritten )
125
+ {
126
+ firstMessageWritten = true ;
127
+ }
128
+ else
129
+ {
130
+ await writer . WriteAsync ( "," ) ;
131
+ await writer . FlushAsync ( ) ;
132
+ }
133
+
134
+ // Use the synchronous serializer in an async context
135
+ await asyncStreamSerializer ( JsonInHtmlWriter , envelope ) ;
136
+ await JsonInHtmlWriter . FlushAsync ( ) ;
137
+ }
138
+
65
139
public void Dispose ( )
66
140
{
67
141
if ( streamClosed ) { return ; }
@@ -86,27 +160,68 @@ public void Dispose()
86
160
streamClosed = true ;
87
161
}
88
162
}
163
+
164
+ public async Task DisposeAsync ( )
165
+ {
166
+ if ( streamClosed ) { return ; }
167
+
168
+ if ( ! preMessageWritten )
169
+ {
170
+ await WritePreMessageAsync ( ) ;
171
+ preMessageWritten = true ;
172
+ }
173
+ if ( ! postMessageWritten )
174
+ {
175
+ await WritePostMessageAsync ( ) ;
176
+ postMessageWritten = true ;
177
+ }
178
+ try
179
+ {
180
+ await writer . FlushAsync ( ) ;
181
+ writer . Close ( ) ;
182
+ }
183
+ finally
184
+ {
185
+ streamClosed = true ;
186
+ }
187
+ }
188
+
89
189
private void WriteResource ( StreamWriter writer , string v )
90
190
{
91
191
var resource = GetResource ( v ) ;
92
192
writer . Write ( resource ) ;
93
193
}
94
194
195
+ private async Task WriteResourceAsync ( StreamWriter writer , string v )
196
+ {
197
+ var resource = GetResource ( v ) ;
198
+ await writer . WriteAsync ( resource ) ;
199
+ }
95
200
private void WriteTemplateBetween ( StreamWriter writer , string template , string ? begin , string ? end )
96
201
{
97
- int beginIndex = begin == null ? 0 : template . IndexOf ( begin ) + begin . Length ;
98
- int endIndex = end == null ? template . Length : template . IndexOf ( end ) ;
99
- int lengthToWrite = endIndex - beginIndex ;
202
+ int beginIndex , lengthToWrite ;
203
+ CalculateBeginAndLength ( template , begin , end , out beginIndex , out lengthToWrite ) ;
100
204
writer . Write ( template . Substring ( beginIndex , lengthToWrite ) ) ;
101
205
}
102
206
207
+ private static void CalculateBeginAndLength ( string template , string ? begin , string ? end , out int beginIndex , out int lengthToWrite )
208
+ {
209
+ beginIndex = begin == null ? 0 : template . IndexOf ( begin ) + begin . Length ;
210
+ int endIndex = end == null ? template . Length : template . IndexOf ( end ) ;
211
+ lengthToWrite = endIndex - beginIndex ;
212
+ }
213
+
214
+ private async Task WriteTemplateBetweenAsync ( StreamWriter writer , string template , string ? begin , string ? end )
215
+ {
216
+ int beginIndex , lengthToWrite ;
217
+ CalculateBeginAndLength ( template , begin , end , out beginIndex , out lengthToWrite ) ;
218
+ await writer . WriteAsync ( template . Substring ( beginIndex , lengthToWrite ) ) ;
219
+ }
103
220
private string GetResource ( string name )
104
221
{
105
222
var assembly = typeof ( MessagesToHtmlWriter ) . Assembly ;
106
223
var resourceStream = assembly . GetManifestResourceStream ( "Cucumber.HtmlFormatter.Resources." + name ) ;
107
224
var resource = new StreamReader ( resourceStream ) . ReadToEnd ( ) ;
108
225
return resource ;
109
226
}
110
-
111
-
112
227
}
0 commit comments