@@ -5,11 +5,14 @@ mod token;
5
5
6
6
use std:: borrow:: Cow ;
7
7
use std:: ffi:: { OsStr , OsString } ;
8
+ use std:: io;
9
+ use std:: iter;
8
10
use std:: path:: { Component , Path , PathBuf , Prefix } ;
9
- use std:: process:: { Command , Stdio } ;
11
+ use std:: process:: Stdio ;
10
12
use std:: sync:: { Arc , Mutex } ;
11
13
12
14
use anyhow:: { bail, Result } ;
15
+ use argmax:: Command ;
13
16
use once_cell:: sync:: Lazy ;
14
17
use regex:: Regex ;
15
18
@@ -89,20 +92,117 @@ impl CommandSet {
89
92
execute_commands ( commands, & out_perm, buffer_output)
90
93
}
91
94
92
- pub fn execute_batch < I > ( & self , paths : I ) -> ExitCode
95
+ pub fn execute_batch < I > ( & self , paths : I , limit : usize ) -> ExitCode
93
96
where
94
97
I : Iterator < Item = PathBuf > ,
95
98
{
96
99
let path_separator = self . path_separator . as_deref ( ) ;
97
- let mut paths = paths. collect :: < Vec < _ > > ( ) ;
98
- paths. sort ( ) ;
99
- for cmd in & self . commands {
100
- let exit = cmd. generate_and_execute_batch ( & paths, path_separator) ;
101
- if exit != ExitCode :: Success {
102
- return exit;
100
+
101
+ let builders: io:: Result < Vec < _ > > = self
102
+ . commands
103
+ . iter ( )
104
+ . map ( |c| CommandBuilder :: new ( c, limit) )
105
+ . collect ( ) ;
106
+
107
+ match builders {
108
+ Ok ( mut builders) => {
109
+ for path in paths {
110
+ for builder in & mut builders {
111
+ if let Err ( e) = builder. push ( & path, path_separator) {
112
+ return handle_cmd_error ( Some ( & builder. cmd ) , e) ;
113
+ }
114
+ }
115
+ }
116
+
117
+ for builder in & mut builders {
118
+ if let Err ( e) = builder. finish ( ) {
119
+ return handle_cmd_error ( Some ( & builder. cmd ) , e) ;
120
+ }
121
+ }
122
+
123
+ ExitCode :: Success
103
124
}
125
+ Err ( e) => handle_cmd_error ( None , e) ,
126
+ }
127
+ }
128
+ }
129
+
130
+ /// Represents a multi-exec command as it is built.
131
+ #[ derive( Debug ) ]
132
+ struct CommandBuilder {
133
+ pre_args : Vec < OsString > ,
134
+ path_arg : ArgumentTemplate ,
135
+ post_args : Vec < OsString > ,
136
+ cmd : Command ,
137
+ count : usize ,
138
+ limit : usize ,
139
+ }
140
+
141
+ impl CommandBuilder {
142
+ fn new ( template : & CommandTemplate , limit : usize ) -> io:: Result < Self > {
143
+ let mut pre_args = vec ! [ ] ;
144
+ let mut path_arg = None ;
145
+ let mut post_args = vec ! [ ] ;
146
+
147
+ for arg in & template. args {
148
+ if arg. has_tokens ( ) {
149
+ path_arg = Some ( arg. clone ( ) ) ;
150
+ } else if path_arg == None {
151
+ pre_args. push ( arg. generate ( "" , None ) ) ;
152
+ } else {
153
+ post_args. push ( arg. generate ( "" , None ) ) ;
154
+ }
155
+ }
156
+
157
+ let cmd = Self :: new_command ( & pre_args) ?;
158
+
159
+ Ok ( Self {
160
+ pre_args,
161
+ path_arg : path_arg. unwrap ( ) ,
162
+ post_args,
163
+ cmd,
164
+ count : 0 ,
165
+ limit,
166
+ } )
167
+ }
168
+
169
+ fn new_command ( pre_args : & [ OsString ] ) -> io:: Result < Command > {
170
+ let mut cmd = Command :: new ( & pre_args[ 0 ] ) ;
171
+ cmd. stdin ( Stdio :: inherit ( ) ) ;
172
+ cmd. stdout ( Stdio :: inherit ( ) ) ;
173
+ cmd. stderr ( Stdio :: inherit ( ) ) ;
174
+ cmd. try_args ( & pre_args[ 1 ..] ) ?;
175
+ Ok ( cmd)
176
+ }
177
+
178
+ fn push ( & mut self , path : & Path , separator : Option < & str > ) -> io:: Result < ( ) > {
179
+ if self . limit > 0 && self . count >= self . limit {
180
+ self . finish ( ) ?;
104
181
}
105
- ExitCode :: Success
182
+
183
+ let arg = self . path_arg . generate ( path, separator) ;
184
+ if !self
185
+ . cmd
186
+ . args_would_fit ( iter:: once ( & arg) . chain ( & self . post_args ) )
187
+ {
188
+ self . finish ( ) ?;
189
+ }
190
+
191
+ self . cmd . try_arg ( arg) ?;
192
+ self . count += 1 ;
193
+ Ok ( ( ) )
194
+ }
195
+
196
+ fn finish ( & mut self ) -> io:: Result < ( ) > {
197
+ if self . count > 0 {
198
+ self . cmd . try_args ( & self . post_args ) ?;
199
+ self . cmd . status ( ) ?;
200
+
201
+ self . cmd = Self :: new_command ( & self . pre_args ) ?;
202
+ self . count = 0 ;
203
+ }
204
+
205
+ Ok ( ( ) )
106
206
}
107
207
}
108
208
@@ -192,45 +292,12 @@ impl CommandTemplate {
192
292
///
193
293
/// Using the internal `args` field, and a supplied `input` variable, a `Command` will be
194
294
/// build.
195
- fn generate ( & self , input : & Path , path_separator : Option < & str > ) -> Command {
295
+ fn generate ( & self , input : & Path , path_separator : Option < & str > ) -> io :: Result < Command > {
196
296
let mut cmd = Command :: new ( self . args [ 0 ] . generate ( & input, path_separator) ) ;
197
297
for arg in & self . args [ 1 ..] {
198
- cmd. arg ( arg. generate ( & input, path_separator) ) ;
199
- }
200
- cmd
201
- }
202
-
203
- fn generate_and_execute_batch (
204
- & self ,
205
- paths : & [ PathBuf ] ,
206
- path_separator : Option < & str > ,
207
- ) -> ExitCode {
208
- let mut cmd = Command :: new ( self . args [ 0 ] . generate ( "" , None ) ) ;
209
- cmd. stdin ( Stdio :: inherit ( ) ) ;
210
- cmd. stdout ( Stdio :: inherit ( ) ) ;
211
- cmd. stderr ( Stdio :: inherit ( ) ) ;
212
-
213
- let mut has_path = false ;
214
-
215
- for arg in & self . args [ 1 ..] {
216
- if arg. has_tokens ( ) {
217
- for path in paths {
218
- cmd. arg ( arg. generate ( path, path_separator) ) ;
219
- has_path = true ;
220
- }
221
- } else {
222
- cmd. arg ( arg. generate ( "" , None ) ) ;
223
- }
224
- }
225
-
226
- if has_path {
227
- match cmd. spawn ( ) . and_then ( |mut c| c. wait ( ) ) {
228
- Ok ( _) => ExitCode :: Success ,
229
- Err ( e) => handle_cmd_error ( & cmd, e) ,
230
- }
231
- } else {
232
- ExitCode :: Success
298
+ cmd. try_arg ( arg. generate ( & input, path_separator) ) ?;
233
299
}
300
+ Ok ( cmd)
234
301
}
235
302
}
236
303
0 commit comments