1- use std:: { fs:: File , io:: prelude:: * , path:: PathBuf } ;
1+ use std:: {
2+ fs:: { File , self } ,
3+ io:: { Write , stdin, stdout} ,
4+ path:: PathBuf ,
5+ borrow:: Cow ,
6+ ops:: DerefMut
7+ } ;
28
39use crate :: { Error , Replacer , Result } ;
410
5- use is_terminal :: IsTerminal ;
11+ use memmap2 :: { Mmap , MmapMut } ;
612
713#[ derive( Debug ) ]
814pub ( crate ) enum Source {
@@ -11,7 +17,7 @@ pub(crate) enum Source {
1117}
1218
1319impl Source {
14- pub ( crate ) fn recursive ( ) -> Result < Self > {
20+ pub ( crate ) fn cwd_recursive ( ) -> Result < Self > {
1521 Ok ( Self :: Files (
1622 ignore:: WalkBuilder :: new ( "." )
1723 . hidden ( false )
@@ -33,83 +39,69 @@ pub(crate) struct App {
3339}
3440
3541impl App {
36- fn stdin_replace ( & self , is_tty : bool ) -> Result < ( ) > {
37- let mut buffer = Vec :: with_capacity ( 256 ) ;
38- let stdin = std:: io:: stdin ( ) ;
39- let mut handle = stdin. lock ( ) ;
40- handle. read_to_end ( & mut buffer) ?;
41-
42- let stdout = std:: io:: stdout ( ) ;
43- let mut handle = stdout. lock ( ) ;
44-
45- handle. write_all ( & if is_tty {
46- self . replacer . replace_preview ( & buffer)
47- } else {
48- self . replacer . replace ( & buffer)
49- } ) ?;
50-
51- Ok ( ( ) )
52- }
53-
5442 pub ( crate ) fn new ( source : Source , replacer : Replacer ) -> Self {
5543 Self { source, replacer }
5644 }
5745 pub ( crate ) fn run ( & self , preview : bool ) -> Result < ( ) > {
58- let is_tty = std:: io:: stdout ( ) . is_terminal ( ) ;
59-
60- match ( & self . source , preview) {
61- ( Source :: Stdin , true ) => self . stdin_replace ( is_tty) ,
62- ( Source :: Stdin , false ) => self . stdin_replace ( is_tty) ,
63- ( Source :: Files ( paths) , false ) => {
64- use rayon:: prelude:: * ;
46+ let sources: Vec < _ > = match & self . source {
47+ Source :: Stdin => vec ! [ ( PathBuf :: from( "STDIN" ) , unsafe { Mmap :: map( & stdin( ) . lock( ) ) ? } ) ] ,
48+ Source :: Files ( paths) => {
49+ let mut mmaps = Vec :: new ( ) ;
50+ for path in paths {
51+ if !path. exists ( ) {
52+ return Err ( Error :: InvalidPath ( path. clone ( ) ) ) ;
53+ }
54+ mmaps. push ( ( path. clone ( ) , unsafe { Mmap :: map ( & File :: open ( path) ?) ? } ) ) ;
55+ }
56+ mmaps
57+ } ,
58+ } ;
59+ let needs_separator = sources. len ( ) > 1 ;
60+
61+ let replaced: Vec < ( & PathBuf , & Mmap , Cow < ' _ , [ u8 ] > ) > = {
62+ use rayon:: prelude:: * ;
63+ sources. par_iter ( )
64+ . map ( |( path, mmap) | {
65+ let replaced = self . replacer . replace ( mmap) ;
66+ ( path, mmap, replaced)
67+ } )
68+ . collect ( )
69+ } ;
6570
66- let failed_jobs: Vec < _ > = paths
67- . par_iter ( )
68- . filter_map ( |p| {
69- if let Err ( e) = self . replacer . replace_file ( p) {
70- Some ( ( p. to_owned ( ) , e) )
71- } else {
72- None
73- }
74- } )
75- . collect ( ) ;
71+ if preview {
72+ let mut handle = stdout ( ) . lock ( ) ;
7673
77- if failed_jobs. is_empty ( ) {
78- Ok ( ( ) )
79- } else {
80- let failed_jobs =
81- crate :: error:: FailedJobs :: from ( failed_jobs) ;
82- Err ( Error :: FailedProcessing ( failed_jobs) )
74+ for ( path, _, piece) in replaced {
75+ if needs_separator {
76+ writeln ! ( handle, "----- FILE {} -----" , path. display( ) ) ?;
8377 }
78+ handle. write_all ( & piece) ?;
79+ writeln ! ( handle) ?;
8480 }
85- ( Source :: Files ( paths) , true ) => {
86- let stdout = std:: io:: stdout ( ) ;
87- let mut handle = stdout. lock ( ) ;
88- let print_path = paths. len ( ) > 1 ;
89-
90- paths. iter ( ) . try_for_each ( |path| {
91- if Replacer :: check_not_empty ( File :: open ( path) ?) . is_err ( ) {
92- return Ok ( ( ) ) ;
93- }
94- let file =
95- unsafe { memmap2:: Mmap :: map ( & File :: open ( path) ?) ? } ;
96- if self . replacer . has_matches ( & file) {
97- if print_path {
98- writeln ! (
99- handle,
100- "----- FILE {} -----" ,
101- path. display( )
102- ) ?;
103- }
104-
105- handle
106- . write_all ( & self . replacer . replace_preview ( & file) ) ?;
107- writeln ! ( handle) ?;
108- }
109-
110- Ok ( ( ) )
111- } )
81+ } else {
82+ for ( path, _, replaced) in replaced {
83+ let source = File :: open ( path) ?;
84+ let meta = fs:: metadata ( path) ?;
85+ drop ( source) ;
86+
87+ let target = tempfile:: NamedTempFile :: new_in (
88+ path. parent ( )
89+ . ok_or_else ( || Error :: InvalidPath ( path. to_path_buf ( ) ) ) ?,
90+ ) ?;
91+ let file = target. as_file ( ) ;
92+ file. set_len ( replaced. len ( ) as u64 ) ?;
93+ file. set_permissions ( meta. permissions ( ) ) ?;
94+
95+ if !replaced. is_empty ( ) {
96+ let mut mmap_target = unsafe { MmapMut :: map_mut ( file) ? } ;
97+ mmap_target. deref_mut ( ) . write_all ( & replaced) ?;
98+ mmap_target. flush_async ( ) ?;
99+ }
100+
101+ target. persist ( fs:: canonicalize ( path) ?) ?;
112102 }
113103 }
104+
105+ Ok ( ( ) )
114106 }
115107}
0 commit comments