@@ -6,23 +6,46 @@ namespace Cucumber.HtmlFormatter;
66public class MessagesToHtmlWriter : IDisposable
77{
88 private StreamWriter writer ;
9+ private Func < StreamWriter , Envelope , Task > asyncStreamSerializer ;
910 private Action < StreamWriter , Envelope > streamSerializer ;
1011 private string template ;
1112 private JsonInHtmlWriter JsonInHtmlWriter ;
1213 private bool streamClosed = false ;
1314 private bool preMessageWritten = false ;
1415 private bool firstMessageWritten = false ;
1516 private bool postMessageWritten = false ;
17+ private bool isAsyncInitialized = false ;
1618
19+ [ Obsolete ( "Cucumber.HtmlFormatter moving to async only operations. Please use the MessagesToHtmlWriter(Stream, Func<StreamWriter, Envelope, Task>) constructor" , false ) ]
1720 public MessagesToHtmlWriter ( Stream stream , Action < StreamWriter , Envelope > streamSerializer ) : this ( new StreamWriter ( stream ) , streamSerializer )
1821 {
1922 }
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 ) ]
2026 public MessagesToHtmlWriter ( StreamWriter writer , Action < StreamWriter , Envelope > streamSerializer )
2127 {
2228 this . writer = writer ;
2329 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+ } ;
2436 template = GetResource ( "index.mustache.html" ) ;
2537 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 ;
2649 }
2750
2851 private void WritePreMessage ( )
@@ -32,15 +55,35 @@ private void WritePreMessage()
3255 WriteTemplateBetween ( writer , template , "{{css}}" , "{{messages}}" ) ;
3356 }
3457
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+
3565 private void WritePostMessage ( )
3666 {
3767 WriteTemplateBetween ( writer , template , "{{messages}}" , "{{script}}" ) ;
3868 WriteResource ( writer , "main.js" ) ;
3969 WriteTemplateBetween ( writer , template , "{{script}}" , null ) ;
4070 }
4171
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+
4279 public void Write ( Envelope envelope )
4380 {
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+
4487 if ( streamClosed ) { throw new IOException ( "Stream closed" ) ; }
4588
4689 if ( ! preMessageWritten )
@@ -62,6 +105,37 @@ public void Write(Envelope envelope)
62105 streamSerializer ( JsonInHtmlWriter , envelope ) ;
63106 JsonInHtmlWriter . Flush ( ) ;
64107 }
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+
65139 public void Dispose ( )
66140 {
67141 if ( streamClosed ) { return ; }
@@ -86,27 +160,68 @@ public void Dispose()
86160 streamClosed = true ;
87161 }
88162 }
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+
89189 private void WriteResource ( StreamWriter writer , string v )
90190 {
91191 var resource = GetResource ( v ) ;
92192 writer . Write ( resource ) ;
93193 }
94194
195+ private async Task WriteResourceAsync ( StreamWriter writer , string v )
196+ {
197+ var resource = GetResource ( v ) ;
198+ await writer . WriteAsync ( resource ) ;
199+ }
95200 private void WriteTemplateBetween ( StreamWriter writer , string template , string ? begin , string ? end )
96201 {
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 ) ;
100204 writer . Write ( template . Substring ( beginIndex , lengthToWrite ) ) ;
101205 }
102206
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+ }
103220 private string GetResource ( string name )
104221 {
105222 var assembly = typeof ( MessagesToHtmlWriter ) . Assembly ;
106223 var resourceStream = assembly . GetManifestResourceStream ( "Cucumber.HtmlFormatter.Resources." + name ) ;
107224 var resource = new StreamReader ( resourceStream ) . ReadToEnd ( ) ;
108225 return resource ;
109226 }
110-
111-
112227}
0 commit comments