@@ -156,8 +156,12 @@ impl FilePatch {
156156 self
157157 }
158158
159- /// Set full replacement content for this file.
160- pub fn with_replacement ( mut self , content : impl Into < String > ) -> Self {
159+ /// Set full replacement content for this file from a slice of lines.
160+ ///
161+ /// Each line is joined with newlines, and a trailing newline is added.
162+ /// All lines must have the same number of columns (commas).
163+ /// Example: `with_replacement(&["header1,header2", "value1,value2"])`
164+ pub fn with_replacement ( mut self , lines : & [ & str ] ) -> Self {
161165 assert ! (
162166 self . header_row. is_none( ) ,
163167 "Cannot set replacement content when header is set for this FilePatch" ,
@@ -170,7 +174,21 @@ impl FilePatch {
170174 self . replacement_content. is_none( ) ,
171175 "Replacement content already set for this FilePatch" ,
172176 ) ;
173- self . replacement_content = Some ( content. into ( ) ) ;
177+
178+ // Validate that all lines have the same number of columns
179+ if !lines. is_empty ( ) {
180+ let first_col_count = lines[ 0 ] . matches ( ',' ) . count ( ) + 1 ;
181+ for ( idx, line) in lines. iter ( ) . enumerate ( ) {
182+ let col_count = line. matches ( ',' ) . count ( ) + 1 ;
183+ assert_eq ! (
184+ col_count, first_col_count,
185+ "Line {idx} has {col_count} columns but line 0 has {first_col_count}: {line:?}"
186+ ) ;
187+ }
188+ }
189+
190+ let content = lines. join ( "\n " ) + "\n " ;
191+ self . replacement_content = Some ( content) ;
174192 self
175193 }
176194
@@ -208,7 +226,8 @@ impl FilePatch {
208226 base_path. display( )
209227 ) ;
210228
211- // nothing further to do if this patch is a full replacement
229+ // If this patch is a full replacement, validate the base file exists
230+ // (checked above) and return the replacement content
212231 if let Some ( content) = & self . replacement_content {
213232 return Ok ( content. clone ( ) ) ;
214233 }
@@ -430,16 +449,18 @@ mod tests {
430449
431450 #[ test]
432451 fn file_patch_with_replacement ( ) {
433- let replacement = "col1,col2\n new1,new2\n " ;
452+ let expected = "col1,col2\n new1,new2\n " ;
434453
435454 let model_dir = ModelPatch :: from_example ( "simple" )
436- . with_file_patch ( FilePatch :: new ( "assets.csv" ) . with_replacement ( replacement) )
455+ . with_file_patch (
456+ FilePatch :: new ( "assets.csv" ) . with_replacement ( & [ "col1,col2" , "new1,new2" ] ) ,
457+ )
437458 . build_to_tempdir ( )
438459 . unwrap ( ) ;
439460
440461 let assets_path = model_dir. path ( ) . join ( "assets.csv" ) ;
441462 let assets_content = std:: fs:: read_to_string ( assets_path) . unwrap ( ) ;
442- assert_eq ! ( assets_content, replacement ) ;
463+ assert_eq ! ( assets_content, expected ) ;
443464 }
444465
445466 #[ test]
@@ -449,7 +470,7 @@ mod tests {
449470 fn file_patch_replacement_after_header_panics ( ) {
450471 let _ = FilePatch :: new ( "assets.csv" )
451472 . with_header ( "col1,col2" )
452- . with_replacement ( "col1,col2\n a,b \n " ) ;
473+ . with_replacement ( & [ "col1,col2" , "a,b" ] ) ;
453474 }
454475
455476 #[ test]
@@ -459,28 +480,35 @@ mod tests {
459480 fn file_patch_replacement_after_addition_panics ( ) {
460481 let _ = FilePatch :: new ( "assets.csv" )
461482 . with_addition ( "a,b" )
462- . with_replacement ( "col1,col2\n a,b \n " ) ;
483+ . with_replacement ( & [ "col1,col2" , "a,b" ] ) ;
463484 }
464485
465486 #[ test]
466487 #[ should_panic( expected = "Cannot add rows when replacement content is set for this FilePatch" ) ]
467488 fn file_patch_addition_after_replacement_panics ( ) {
468489 let _ = FilePatch :: new ( "assets.csv" )
469- . with_replacement ( "col1,col2\n a,b \n " )
490+ . with_replacement ( & [ "col1,col2" , "a,b" ] )
470491 . with_addition ( "c,d" ) ;
471492 }
472493
473494 #[ test]
474495 fn file_patch_with_replacement_missing_base_file_fails ( ) {
475- let model_patch = ModelPatch :: from_example ( "simple" )
476- . with_file_patch ( FilePatch :: new ( "not_a_real_file.csv" ) . with_replacement ( "x,y\n 1,2\n " ) ) ;
496+ let model_patch = ModelPatch :: from_example ( "simple" ) . with_file_patch (
497+ FilePatch :: new ( "not_a_real_file.csv" ) . with_replacement ( & [ "x,y" , "1,2" ] ) ,
498+ ) ;
477499
478500 assert_error ! (
479501 model_patch. build_to_tempdir( ) ,
480502 "Base file for patching does not exist: examples/simple/not_a_real_file.csv"
481503 ) ;
482504 }
483505
506+ #[ test]
507+ #[ should_panic( expected = "Line 1 has 2 columns but line 0 has 3" ) ]
508+ fn file_patch_replacement_column_count_mismatch_panics ( ) {
509+ let _ = FilePatch :: new ( "test.csv" ) . with_replacement ( & [ "col1,col2,col3" , "a,b" ] ) ;
510+ }
511+
484512 #[ test]
485513 fn toml_patch ( ) {
486514 // Patch to add an extra milestone year (2050)
0 commit comments