@@ -47,7 +47,7 @@ pub(crate) fn create_diff(
4747 "\n Difference({} / {}):{}" ,
4848 LineStyle :: extra_actual_style( ) . style( "actual" ) ,
4949 LineStyle :: extra_expected_style( ) . style( "expected" ) ,
50- edit_list_summary ( & edit_list)
50+ edit_list. into_iter ( ) . collect :: < BufferedSummary > ( ) ,
5151 )
5252 . into ( ) ,
5353 edit_distance:: Difference :: Unrelated => "" . into ( ) ,
@@ -83,73 +83,150 @@ pub(crate) fn create_diff_reversed(
8383 "\n Difference({} / {}):{}" ,
8484 LineStyle :: extra_actual_style( ) . style( "actual" ) ,
8585 LineStyle :: extra_expected_style( ) . style( "expected" ) ,
86- edit_list_summary ( & edit_list)
86+ edit_list. into_iter ( ) . collect :: < BufferedSummary > ( ) ,
8787 )
8888 . into ( )
8989 }
9090 edit_distance:: Difference :: Unrelated => "" . into ( ) ,
9191 }
9292}
9393
94- fn edit_list_summary ( edit_list : & [ edit_distance:: Edit < & str > ] ) -> String {
95- let mut summary = String :: new ( ) ;
96- // Use to collect common line and compress them.
97- let mut common_line_buffer = vec ! [ ] ;
98- for edit in edit_list {
99- let ( style, line) = match edit {
100- edit_distance:: Edit :: Both ( same) => {
101- common_line_buffer. push ( * same) ;
102- continue ;
103- }
104- edit_distance:: Edit :: ExtraActual ( actual) => ( LineStyle :: extra_actual_style ( ) , * actual) ,
105- edit_distance:: Edit :: ExtraExpected ( expected) => {
106- ( LineStyle :: extra_expected_style ( ) , * expected)
107- }
108- edit_distance:: Edit :: AdditionalActual => {
109- ( LineStyle :: comment_style ( ) , "<---- remaining lines omitted ---->" )
110- }
111- } ;
112- summary. push_str ( & compress_common_lines ( std:: mem:: take ( & mut common_line_buffer) ) ) ;
94+ // Aggregator collecting the lines to be printed in the difference summary.
95+ //
96+ // This is buffered in order to allow a future line to potentially impact how
97+ // the current line would be printed.
98+ struct BufferedSummary < ' a > {
99+ summary : String ,
100+ buffer : Buffer < ' a > ,
101+ }
102+
103+ impl < ' a > BufferedSummary < ' a > {
104+ // Appends a new line which is common to both actual and expected.
105+ fn feed_common_lines ( & mut self , common_line : & ' a str ) {
106+ let Buffer :: CommonLineBuffer ( ref mut common_lines) = self . buffer ;
107+ common_lines. push ( common_line) ;
108+ }
109+
110+ // Appends a new line which is found only in the actual string.
111+ fn feed_extra_actual ( & mut self , extra_actual : & ' a str ) {
112+ self . buffer . flush ( & mut self . summary ) . unwrap ( ) ;
113+ write ! ( & mut self . summary, "\n {}" , LineStyle :: extra_actual_style( ) . style( extra_actual) )
114+ . unwrap ( ) ;
115+ }
113116
114- write ! ( & mut summary, "\n {}" , style. style( line) ) . unwrap ( ) ;
117+ // Appends a new line which is found only in the expected string.
118+ fn feed_extra_expected ( & mut self , extra_expected : & str ) {
119+ self . flush_buffer ( ) ;
120+ write ! ( & mut self . summary, "\n {}" , LineStyle :: extra_expected_style( ) . style( extra_expected) )
121+ . unwrap ( ) ;
115122 }
116- summary. push_str ( & compress_common_lines ( common_line_buffer) ) ;
117123
118- summary
124+ // Appends a comment for the additional line at the start or the end of the
125+ // actual string which should be omitted.
126+ fn feed_additional_actual ( & mut self ) {
127+ self . flush_buffer ( ) ;
128+ write ! (
129+ & mut self . summary,
130+ "\n {}" ,
131+ LineStyle :: comment_style( ) . style( "<---- remaining lines omitted ---->" )
132+ )
133+ . unwrap ( ) ;
134+ }
135+
136+ fn flush_buffer ( & mut self ) {
137+ self . buffer . flush ( & mut self . summary ) . unwrap ( ) ;
138+ }
119139}
120140
121- // The number of the lines kept before and after the compressed lines.
122- const COMMON_LINES_CONTEXT_SIZE : usize = 2 ;
141+ impl < ' a > FromIterator < edit_distance:: Edit < & ' a str > > for BufferedSummary < ' a > {
142+ fn from_iter < T : IntoIterator < Item = edit_distance:: Edit < & ' a str > > > ( iter : T ) -> Self {
143+ let mut buffered_summary =
144+ BufferedSummary { summary : String :: new ( ) , buffer : Buffer :: CommonLineBuffer ( vec ! [ ] ) } ;
145+ for edit in iter {
146+ match edit {
147+ edit_distance:: Edit :: Both ( same) => {
148+ buffered_summary. feed_common_lines ( same) ;
149+ }
150+ edit_distance:: Edit :: ExtraActual ( actual) => {
151+ buffered_summary. feed_extra_actual ( actual) ;
152+ }
153+ edit_distance:: Edit :: ExtraExpected ( expected) => {
154+ buffered_summary. feed_extra_expected ( expected) ;
155+ }
156+ edit_distance:: Edit :: AdditionalActual => {
157+ buffered_summary. feed_additional_actual ( ) ;
158+ }
159+ } ;
160+ }
161+ buffered_summary. flush_buffer ( ) ;
123162
124- fn compress_common_lines ( common_lines : Vec < & str > ) -> String {
125- if common_lines. len ( ) <= 2 * COMMON_LINES_CONTEXT_SIZE + 1 {
126- let mut all_lines = String :: new ( ) ;
127- for line in common_lines {
128- write ! ( & mut all_lines, "\n {}" , LineStyle :: unchanged_style( ) . style( line) ) . unwrap ( ) ;
163+ buffered_summary
164+ }
165+ }
166+
167+ impl < ' a > Display for BufferedSummary < ' a > {
168+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
169+ if !matches ! ( self . buffer, Buffer :: CommonLineBuffer ( ref b) if b. is_empty( ) ) {
170+ panic ! ( "Buffer is not empty. This is a bug in gtest_rust." )
129171 }
130- return all_lines ;
172+ self . summary . fmt ( f )
131173 }
174+ }
132175
133- let mut truncated_lines = String :: new ( ) ;
176+ // This needs to be an enum as there will be in a follow-up PR new types of
177+ // buffer, most likely actual and expected lines, to be compared with expected
178+ // and actual lines for line to line comparison.
179+ enum Buffer < ' a > {
180+ CommonLineBuffer ( Vec < & ' a str > ) ,
181+ }
134182
135- for line in & common_lines[ 0 ..COMMON_LINES_CONTEXT_SIZE ] {
136- write ! ( & mut truncated_lines, "\n {}" , LineStyle :: unchanged_style( ) . style( line) ) . unwrap ( ) ;
183+ impl < ' a > Buffer < ' a > {
184+ fn flush ( & mut self , writer : impl std:: fmt:: Write ) -> std:: fmt:: Result {
185+ match self {
186+ Buffer :: CommonLineBuffer ( common_lines) => {
187+ Self :: flush_common_lines ( std:: mem:: take ( common_lines) , writer) ?
188+ }
189+ } ;
190+ Ok ( ( ) )
137191 }
138192
139- write ! (
140- & mut truncated_lines,
141- "\n {}" ,
142- LineStyle :: comment_style( ) . style( & format!(
143- "<---- {} common lines omitted ---->" ,
144- common_lines. len( ) - 2 * COMMON_LINES_CONTEXT_SIZE
145- ) ) ,
146- )
147- . unwrap ( ) ;
148-
149- for line in & common_lines[ common_lines. len ( ) - COMMON_LINES_CONTEXT_SIZE ..common_lines. len ( ) ] {
150- write ! ( & mut truncated_lines, "\n {}" , LineStyle :: unchanged_style( ) . style( line) ) . unwrap ( ) ;
193+ fn flush_common_lines (
194+ common_lines : Vec < & ' a str > ,
195+ mut writer : impl std:: fmt:: Write ,
196+ ) -> std:: fmt:: Result {
197+ // The number of the lines kept before and after the compressed lines.
198+ const COMMON_LINES_CONTEXT_SIZE : usize = 2 ;
199+
200+ if common_lines. len ( ) <= 2 * COMMON_LINES_CONTEXT_SIZE + 1 {
201+ for line in common_lines {
202+ write ! ( writer, "\n {}" , LineStyle :: unchanged_style( ) . style( line) ) ?;
203+ }
204+ return Ok ( ( ) ) ;
205+ }
206+
207+ let start_context = & common_lines[ 0 ..COMMON_LINES_CONTEXT_SIZE ] ;
208+
209+ for line in start_context {
210+ write ! ( writer, "\n {}" , LineStyle :: unchanged_style( ) . style( line) ) ?;
211+ }
212+
213+ write ! (
214+ writer,
215+ "\n {}" ,
216+ LineStyle :: comment_style( ) . style( & format!(
217+ "<---- {} common lines omitted ---->" ,
218+ common_lines. len( ) - 2 * COMMON_LINES_CONTEXT_SIZE
219+ ) ) ,
220+ ) ?;
221+
222+ let end_context =
223+ & common_lines[ common_lines. len ( ) - COMMON_LINES_CONTEXT_SIZE ..common_lines. len ( ) ] ;
224+
225+ for line in end_context {
226+ write ! ( writer, "\n {}" , LineStyle :: unchanged_style( ) . style( line) ) ?;
227+ }
228+ Ok ( ( ) )
151229 }
152- truncated_lines
153230}
154231
155232// Use ANSI code to enable styling on the summary lines.
0 commit comments