1- use std:: { fs:: File , io:: prelude:: * , path:: PathBuf } ;
1+ use std:: {
2+ fs:: { File , self } ,
3+ io:: { Write , stdin, stdout, Read } ,
4+ path:: PathBuf ,
5+ ops:: DerefMut ,
6+ } ;
27
38use crate :: { Error , Replacer , Result } ;
49
5- use is_terminal :: IsTerminal ;
10+ use memmap2 :: { Mmap , MmapMut , MmapOptions } ;
611
7- #[ derive( Debug ) ]
12+ #[ derive( Debug , PartialEq ) ]
813pub ( crate ) enum Source {
914 Stdin ,
1015 Files ( Vec < PathBuf > ) ,
@@ -16,83 +21,80 @@ pub(crate) struct App {
1621}
1722
1823impl App {
19- fn stdin_replace ( & self , is_tty : bool ) -> Result < ( ) > {
20- let mut buffer = Vec :: with_capacity ( 256 ) ;
21- let stdin = std:: io:: stdin ( ) ;
22- let mut handle = stdin. lock ( ) ;
23- handle. read_to_end ( & mut buffer) ?;
24-
25- let stdout = std:: io:: stdout ( ) ;
26- let mut handle = stdout. lock ( ) ;
27-
28- handle. write_all ( & if is_tty {
29- self . replacer . replace_preview ( & buffer)
30- } else {
31- self . replacer . replace ( & buffer)
32- } ) ?;
33-
34- Ok ( ( ) )
35- }
36-
3724 pub ( crate ) fn new ( source : Source , replacer : Replacer ) -> Self {
3825 Self { source, replacer }
3926 }
27+
4028 pub ( crate ) fn run ( & self , preview : bool ) -> Result < ( ) > {
41- let is_tty = std:: io:: stdout ( ) . is_terminal ( ) ;
29+ let sources: Vec < ( PathBuf , Mmap ) > = match & self . source {
30+ Source :: Stdin => {
31+ let mut handle = stdin ( ) . lock ( ) ;
32+ let mut buf = Vec :: new ( ) ;
33+ handle. read_to_end ( & mut buf) ?;
34+ let mut mmap = MmapOptions :: new ( )
35+ . len ( buf. len ( ) )
36+ . map_anon ( ) ?;
37+ mmap. copy_from_slice ( & buf) ;
38+ let mmap = mmap. make_read_only ( ) ?;
39+ vec ! [ ( PathBuf :: from( "STDIN" ) , mmap) ]
40+ } ,
41+ Source :: Files ( paths) => {
42+ let mut refs = Vec :: new ( ) ;
43+ for path in paths {
44+ if !path. exists ( ) {
45+ return Err ( Error :: InvalidPath ( path. clone ( ) ) ) ;
46+ }
47+ let mmap = unsafe { Mmap :: map ( & File :: open ( path) ?) ? } ;
48+ refs. push ( ( path. clone ( ) , mmap) ) ;
49+ }
50+ refs
51+ } ,
52+ } ;
53+ let needs_separator = sources. len ( ) > 1 ;
4254
43- match ( & self . source , preview) {
44- ( Source :: Stdin , true ) => self . stdin_replace ( is_tty) ,
45- ( Source :: Stdin , false ) => self . stdin_replace ( is_tty) ,
46- ( Source :: Files ( paths) , false ) => {
47- use rayon:: prelude:: * ;
55+ let replaced: Vec < _ > = {
56+ use rayon:: prelude:: * ;
57+ sources. par_iter ( )
58+ . map ( |( path, mmap) | {
59+ let replaced = self . replacer . replace ( mmap) ;
60+ ( path, mmap, replaced)
61+ } )
62+ . collect ( )
63+ } ;
4864
49- let failed_jobs: Vec < _ > = paths
50- . par_iter ( )
51- . filter_map ( |p| {
52- if let Err ( e) = self . replacer . replace_file ( p) {
53- Some ( ( p. to_owned ( ) , e) )
54- } else {
55- None
56- }
57- } )
58- . collect ( ) ;
65+ if preview || self . source == Source :: Stdin {
66+ let mut handle = stdout ( ) . lock ( ) ;
5967
60- if failed_jobs. is_empty ( ) {
61- Ok ( ( ) )
62- } else {
63- let failed_jobs =
64- crate :: error:: FailedJobs :: from ( failed_jobs) ;
65- Err ( Error :: FailedProcessing ( failed_jobs) )
68+ for ( path, _, replaced) in replaced {
69+ if needs_separator {
70+ writeln ! ( handle, "----- FILE {} -----" , path. display( ) ) ?;
6671 }
72+ handle. write_all ( replaced. as_ref ( ) ) ?;
6773 }
68- ( Source :: Files ( paths) , true ) => {
69- let stdout = std:: io:: stdout ( ) ;
70- let mut handle = stdout. lock ( ) ;
71- let print_path = paths. len ( ) > 1 ;
72-
73- paths. iter ( ) . try_for_each ( |path| {
74- if Replacer :: check_not_empty ( File :: open ( path) ?) . is_err ( ) {
75- return Ok ( ( ) ) ;
76- }
77- let file =
78- unsafe { memmap2:: Mmap :: map ( & File :: open ( path) ?) ? } ;
79- if self . replacer . has_matches ( & file) {
80- if print_path {
81- writeln ! (
82- handle,
83- "----- FILE {} -----" ,
84- path. display( )
85- ) ?;
86- }
74+ } else {
75+ for ( path, _, replaced) in replaced {
76+ let source = File :: open ( path) ?;
77+ let meta = fs:: metadata ( path) ?;
78+ drop ( source) ;
8779
88- handle
89- . write_all ( & self . replacer . replace_preview ( & file) ) ?;
90- writeln ! ( handle) ?;
91- }
80+ let target = tempfile:: NamedTempFile :: new_in (
81+ path. parent ( )
82+ . ok_or_else ( || Error :: InvalidPath ( path. to_path_buf ( ) ) ) ?,
83+ ) ?;
84+ let file = target. as_file ( ) ;
85+ file. set_len ( replaced. len ( ) as u64 ) ?;
86+ file. set_permissions ( meta. permissions ( ) ) ?;
9287
93- Ok ( ( ) )
94- } )
88+ if !replaced. is_empty ( ) {
89+ let mut mmap_target = unsafe { MmapMut :: map_mut ( file) ? } ;
90+ mmap_target. deref_mut ( ) . write_all ( & replaced) ?;
91+ mmap_target. flush_async ( ) ?;
92+ }
93+
94+ target. persist ( fs:: canonicalize ( path) ?) ?;
9595 }
9696 }
97+
98+ Ok ( ( ) )
9799 }
98100}
0 commit comments