33use std:: {
44 fmt:: Debug ,
55 fs,
6+ io:: Write ,
67 num:: NonZeroU32 ,
78 ops:: RangeInclusive ,
89 path:: { Path , PathBuf } ,
@@ -16,7 +17,7 @@ use crate::{
1617 clang_tools:: {
1718 ReviewComments , Suggestion , clang_format:: FormatAdvice , clang_tidy:: TidyAdvice , make_patch,
1819 } ,
19- cli:: LinesChangedOnly ,
20+ cli:: { ClangParams , LinesChangedOnly } ,
2021 error:: FileObjError ,
2122} ;
2223
@@ -162,6 +163,57 @@ impl FileObj {
162163 false
163164 }
164165
166+ /// If the file has a cached fixes, then append them to a unified patched file.
167+ ///
168+ /// This is the alternative to [`FileObj::make_suggestions_from_patch()`] when
169+ /// a PR review is not being posted. Both function have to create a patch by
170+ /// reading the original file and patched file (in cache), but
171+ /// [`FileObj::make_suggestions_from_patch()`] does more with the diff than this function.
172+ pub fn maybe_append_patch ( & self , repo_root : & Path ) -> Result < ( ) , FileObjError > {
173+ let patched = match & self . patched_path {
174+ Some ( patched_path) if patched_path. exists ( ) => {
175+ fs:: read_to_string ( patched_path) . map_err ( FileObjError :: ReadFile ) ?
176+ }
177+ _ => return Ok ( ( ) ) ,
178+ } ;
179+ let original_content =
180+ fs:: read_to_string ( repo_root. join ( & self . name ) ) . map_err ( FileObjError :: ReadFile ) ?;
181+ let ( diff, input) = make_patch ( patched. as_str ( ) , & original_content) ;
182+ let file_name = self . name . to_string_lossy ( ) . replace ( "\\ " , "/" ) ;
183+ Self :: append_patch ( & file_name, & input, & diff, repo_root) ?;
184+ Ok ( ( ) )
185+ }
186+
187+ /// write fixes to a unified patch file in the cache directory.
188+ fn append_patch (
189+ file_name : & str ,
190+ input : & InternedInput < & str > ,
191+ diff : & Diff ,
192+ repo_root : & Path ,
193+ ) -> Result < ( ) , FileObjError > {
194+ let printer = BasicLineDiffPrinter ( & input. interner ) ;
195+ let mut diff_config = UnifiedDiffConfig :: default ( ) ;
196+ diff_config. context_len ( 0 ) ;
197+ let unified_diff = diff. unified_diff ( & printer, diff_config, input) . to_string ( ) ;
198+ if !unified_diff. is_empty ( ) {
199+ let patch_path_parent = repo_root. join ( ClangParams :: CACHE_DIR ) ;
200+ fs:: create_dir_all ( & patch_path_parent) . map_err ( FileObjError :: MkDirFailed ) ?;
201+ let patch_file_path = patch_path_parent. join ( ClangParams :: AUTO_FIX_PATCH ) ;
202+ let mut patch_file = fs:: OpenOptions :: new ( )
203+ . append ( true )
204+ . create ( true )
205+ . truncate ( false )
206+ . open ( & patch_file_path)
207+ . map_err ( FileObjError :: OpenPatchFileFailed ) ?;
208+ patch_file
209+ . write_all (
210+ format ! ( "--- a/{file_name}\n +++ b/{file_name}\n {unified_diff}" , ) . as_bytes ( ) ,
211+ )
212+ . map_err ( FileObjError :: WritePatchFailed ) ?;
213+ }
214+ Ok ( ( ) )
215+ }
216+
165217 /// Create a list of [`Suggestion`](struct@crate::clang_tools::Suggestion) from a
166218 /// generated diff and store them in the given
167219 /// [`ReviewComments`](struct@crate::clang_tools::ReviewComments).
@@ -183,7 +235,8 @@ impl FileObj {
183235 let original_content =
184236 fs:: read_to_string ( repo_root. join ( & self . name ) ) . map_err ( FileObjError :: ReadFile ) ?;
185237 let ( diff, input) = make_patch ( patched. as_str ( ) , & original_content) ;
186- let file_name = self . name . to_str ( ) . unwrap_or_default ( ) . replace ( "\\ " , "/" ) ;
238+ let file_name = self . name . to_string_lossy ( ) . replace ( "\\ " , "/" ) ;
239+ Self :: append_patch ( & file_name, & input, & diff, repo_root) ?;
187240
188241 self . get_suggestions ( review_comments, & diff, & input, summary_only)
189242 . map_err ( FileObjError :: DisplayStringFailed ) ?;
0 commit comments