@@ -42,27 +42,20 @@ async Task<VerifyResult> VerifyInner(object? root, Func<Task>? cleanup, IEnumera
4242 // Check if root is in targets list (to avoid duplicate processing)
4343 var rootInTargets = root != null && list . Any ( t => ReferenceEquals ( t . Data , root ) ) ;
4444
45- // Process stream converters (may produce converter info that becomes root)
46- var converted = new List < Target > ( ) ;
45+ // Resolve all targets and collect converter infos
4746 var converterInfos = new List < object > ( ) ;
47+ var resolvedTargets = new List < ( ResolvedTarget target , bool applyScrubbers ) > ( ) ;
48+
4849 foreach ( var target in list )
4950 {
50- if ( ! target . PerformConversion ||
51- target . Extension is null ||
52- ! VerifierSettings . HasStreamConverter ( target . Extension ) )
53- {
54- converted . Add ( target ) ;
55- continue ;
56- }
51+ var ( resolvedList , resolveCleanup , applyScrubbers , infos ) = await ResolveTarget ( target ) ;
52+ cleanup += resolveCleanup ;
53+ converterInfos . AddRange ( infos ) ;
5754
58- var ( info , convTargets , itemCleanup ) = await DoExtensionConversion ( target . Extension , target . StreamData , null , target . Name ) ;
59- cleanup += itemCleanup ;
60- if ( info != null )
55+ foreach ( var resolved in resolvedList )
6156 {
62- converterInfos . Add ( info ) ;
57+ resolvedTargets . Add ( ( resolved , applyScrubbers ) ) ;
6358 }
64-
65- converted . AddRange ( convTargets ) ;
6659 }
6760
6861 // If root is null and converters returned info, use converter info as root (for appenders)
@@ -87,44 +80,45 @@ target.Extension is null ||
8780 JsonFormatter . AsJson ( settings , counter , info ) ) ) ;
8881 }
8982
90- // Resolve remaining targets and apply scrubbers inline
91- foreach ( var target in converted )
83+ // Add resolved targets and apply scrubbers
84+ foreach ( var ( resolved , applyScrubbers ) in resolvedTargets )
9285 {
93- var ( resolvedList , resolveCleanup , applyScrubbers ) = await ResolveTarget ( target ) ;
94- cleanup += resolveCleanup ;
95-
96- foreach ( var resolved in resolvedList )
86+ if ( applyScrubbers && resolved . TryGetStringBuilder ( out var builder ) )
9787 {
98- if ( applyScrubbers && resolved . TryGetStringBuilder ( out var builder ) )
99- {
100- ApplyScrubbers . ApplyForExtension ( resolved . Extension , builder , settings , counter ) ;
101- }
102-
103- resultTargets . Add ( resolved ) ;
88+ ApplyScrubbers . ApplyForExtension ( resolved . Extension , builder , settings , counter ) ;
10489 }
90+
91+ resultTargets . Add ( resolved ) ;
10592 }
10693
10794 return ( cleanup , resultTargets ) ;
10895 }
10996
110- async Task < ( List < ResolvedTarget > targets , Func < Task > cleanup , bool applyScrubbers ) > ResolveTarget ( Target target )
97+ async Task < ( List < ResolvedTarget > targets , Func < Task > cleanup , bool applyScrubbers , List < object > converterInfos ) > ResolveTarget ( Target target )
11198 {
11299 var cleanup = ( ) => Task . CompletedTask ;
113100 var results = new List < ResolvedTarget > ( ) ;
101+ var converterInfos = new List < object > ( ) ;
114102
115- // If target has an extension, it's already resolved (Stream or StringBuilder)
116- // These come from converters or explicit targets, so apply scrubbers
103+ // If target has an extension with stream data, check for stream converter
117104 if ( target . Extension is not null )
118105 {
119106 if ( target . TryGetStream ( out var stream ) )
120107 {
108+ if ( target . PerformConversion )
109+ {
110+ var ( streamResults , streamCleanup , infos ) = await ResolveStream ( stream , target . Extension , target . Name ) ;
111+ return ( streamResults , streamCleanup , true , infos ) ;
112+ }
113+
121114 results . Add ( new ( target . Extension , stream , target . Name ) ) ;
122115 }
123116 else if ( target . TryGetStringBuilder ( out var sb ) )
124117 {
125118 results . Add ( new ( target . Extension , sb , target . Name ) ) ;
126119 }
127- return ( results , cleanup , true ) ;
120+
121+ return ( results , cleanup , true , converterInfos ) ;
128122 }
129123
130124 // Target contains an arbitrary object - resolve it using full type matching
@@ -135,15 +129,15 @@ target.Extension is null ||
135129 results . Add ( new (
136130 settings . TxtOrJson ,
137131 JsonFormatter . AsJson ( settings , counter , new InfoBuilder ( false , null , [ ] ) ) ) ) ;
138- return ( results , cleanup , false ) ;
132+ return ( results , cleanup , false , converterInfos ) ;
139133 }
140134
141135 // Handle XContainer (XDocument, XElement) - apply scrubbers
142136 if ( data is XContainer container )
143137 {
144138 var xmlString = ConvertXmlToString ( container ) ;
145139 results . Add ( new ( "xml" , xmlString , target . Name ) ) ;
146- return ( results , cleanup , true ) ;
140+ return ( results , cleanup , true , converterInfos ) ;
147141 }
148142
149143 // Handle XmlNode - apply scrubbers
@@ -154,30 +148,30 @@ target.Extension is null ||
154148 var xdoc = XDocument . Load ( reader ) ;
155149 var xmlString = ConvertXmlToString ( xdoc ) ;
156150 results . Add ( new ( "xml" , xmlString , target . Name ) ) ;
157- return ( results , cleanup , true ) ;
151+ return ( results , cleanup , true , converterInfos ) ;
158152 }
159153
160154 // Handle FileStream (get extension from filename) - apply scrubbers
161155 if ( data is FileStream fileStream )
162156 {
163157 var extension = fileStream . Extension ( ) ;
164- var ( streamResults , streamCleanup ) = await ResolveStream ( fileStream , extension , target . Name ) ;
165- return ( streamResults , streamCleanup , true ) ;
158+ var ( streamResults , streamCleanup , infos ) = await ResolveStream ( fileStream , extension , target . Name ) ;
159+ return ( streamResults , streamCleanup , true , infos ) ;
166160 }
167161
168162 // Handle Stream - apply scrubbers
169163 if ( data is Stream stream2 )
170164 {
171- var ( streamResults , streamCleanup ) = await ResolveStream ( stream2 , "bin" , target . Name ) ;
172- return ( streamResults , streamCleanup , true ) ;
165+ var ( streamResults , streamCleanup , infos ) = await ResolveStream ( stream2 , "bin" , target . Name ) ;
166+ return ( streamResults , streamCleanup , true , infos ) ;
173167 }
174168
175169 // Handle byte[] - apply scrubbers
176170 if ( data is byte [ ] bytes )
177171 {
178172 var memStream = new MemoryStream ( bytes ) ;
179- var ( streamResults , streamCleanup ) = await ResolveStream ( memStream , "bin" , target . Name ) ;
180- return ( streamResults , streamCleanup , true ) ;
173+ var ( streamResults , streamCleanup , infos ) = await ResolveStream ( memStream , "bin" , target . Name ) ;
174+ return ( streamResults , streamCleanup , true , infos ) ;
181175 }
182176
183177 // Handle IEnumerable<Stream> - throw error as in Verify(object)
@@ -190,7 +184,7 @@ target.Extension is null ||
190184 if ( data is StringBuilder sb2 )
191185 {
192186 results . Add ( new ( "txt" , sb2 , target . Name ) ) ;
193- return ( results , cleanup , true ) ;
187+ return ( results , cleanup , true , converterInfos ) ;
194188 }
195189
196190 // Handle string - check for JSON appenders first (matches TryGetRootTarget behavior)
@@ -209,12 +203,12 @@ target.Extension is null ||
209203 results . Add ( new (
210204 settings . TxtOrJson ,
211205 JsonFormatter . AsJson ( settings , counter , new InfoBuilder ( false , str , appends ) ) ) ) ;
212- return ( results , cleanup , false ) ;
206+ return ( results , cleanup , false , converterInfos ) ;
213207 }
214208
215209 // Plain text - apply scrubbers
216210 results . Add ( new ( "txt" , str , target . Name ) ) ;
217- return ( results , cleanup , true ) ;
211+ return ( results , cleanup , true , converterInfos ) ;
218212 }
219213
220214 // Try ToString converter (before typed converter, matching Verify(object) order) - apply scrubbers
@@ -223,7 +217,7 @@ target.Extension is null ||
223217 var stringResult = toString ( data , settings . Context ) ;
224218 var extension = stringResult . Extension ?? "txt" ;
225219 results . Add ( new ( extension , stringResult . Value , target . Name ) ) ;
226- return ( results , cleanup , true ) ;
220+ return ( results , cleanup , true , converterInfos ) ;
227221 }
228222
229223 // Try typed converter - apply scrubbers
@@ -238,10 +232,10 @@ target.Extension is null ||
238232 // Recursively resolve the conversion result targets
239233 foreach ( var convTarget in conversionResult . Targets )
240234 {
241- // Note: We ignore the applyScrubbers from recursive calls since converter output should be scrubbed
242- var ( resolved , resolveCleanup , _) = await ResolveTarget ( convTarget ) ;
235+ var ( resolved , resolveCleanup , _, nestedInfos ) = await ResolveTarget ( convTarget ) ;
243236 cleanup += resolveCleanup ;
244237 results . AddRange ( resolved ) ;
238+ converterInfos . AddRange ( nestedInfos ) ;
245239 }
246240
247241 // Add info as a separate target if present
@@ -261,21 +255,22 @@ target.Extension is null ||
261255 JsonFormatter . AsJson ( settings , counter , new InfoBuilder ( true , null , converterAppends ) ) ) ) ;
262256 }
263257
264- return ( results , cleanup , true ) ;
258+ return ( results , cleanup , true , converterInfos ) ;
265259 }
266260
267261 // Fall back to JSON serialization with appenders - NO scrubbing (matches original TryGetRootTarget behavior)
268262 var jsonAppends = VerifierSettings . GetJsonAppenders ( settings ) ;
269263 results . Add ( new (
270264 settings . TxtOrJson ,
271265 JsonFormatter . AsJson ( settings , counter , new InfoBuilder ( true , data , jsonAppends ) ) ) ) ;
272- return ( results , cleanup , false ) ;
266+ return ( results , cleanup , false , converterInfos ) ;
273267 }
274268
275- async Task < ( List < ResolvedTarget > targets , Func < Task > cleanup ) > ResolveStream ( Stream stream , string extension , string ? name )
269+ async Task < ( List < ResolvedTarget > targets , Func < Task > cleanup , List < object > converterInfos ) > ResolveStream ( Stream stream , string extension , string ? name )
276270 {
277271 var cleanup = ( ) => Task . CompletedTask ;
278272 var results = new List < ResolvedTarget > ( ) ;
273+ var converterInfos = new List < object > ( ) ;
279274
280275 stream . MoveToStart ( ) ;
281276
@@ -285,23 +280,21 @@ target.Extension is null ||
285280 var ( info , converted , convCleanup ) = await DoExtensionConversion ( extension , stream , null , name ) ;
286281 cleanup += convCleanup ;
287282
288- // Add info target if present
289283 if ( info != null )
290284 {
291- results . Add ( new (
292- settings . TxtOrJson ,
293- JsonFormatter . AsJson ( settings , counter , info ) ) ) ;
285+ converterInfos . Add ( info ) ;
294286 }
295287
296288 // Recursively resolve converted targets (they may contain objects)
297289 foreach ( var convTarget in converted )
298290 {
299- var ( resolved , resolveCleanup , _) = await ResolveTarget ( convTarget ) ;
291+ var ( resolved , resolveCleanup , _, nestedInfos ) = await ResolveTarget ( convTarget ) ;
300292 cleanup += resolveCleanup ;
301293 results . AddRange ( resolved ) ;
294+ converterInfos . AddRange ( nestedInfos ) ;
302295 }
303296
304- return ( results , cleanup ) ;
297+ return ( results , cleanup , converterInfos ) ;
305298 }
306299
307300 // No converter - convert stream directly to ResolvedTarget
@@ -314,7 +307,7 @@ target.Extension is null ||
314307 results . Add ( new ( extension , stream , name ) ) ;
315308 }
316309
317- return ( results , cleanup ) ;
310+ return ( results , cleanup , converterInfos ) ;
318311 }
319312
320313 string ConvertXmlToString ( XContainer target )
0 commit comments