@@ -3,7 +3,7 @@ use crate::{
3
3
forge:: { Forge , ForgeType } ,
4
4
github:: GitHub ,
5
5
gitlab:: GitLab ,
6
- installer:: Installer ,
6
+ installer:: { ArchiveInstaller , ExeInstaller , Installer } ,
7
7
picker:: AssetPicker ,
8
8
ubi:: Ubi ,
9
9
} ;
@@ -32,6 +32,7 @@ pub struct UbiBuilder<'a> {
32
32
install_dir : Option < PathBuf > ,
33
33
matching : Option < & ' a str > ,
34
34
exe : Option < & ' a str > ,
35
+ extract_all : bool ,
35
36
github_token : Option < & ' a str > ,
36
37
gitlab_token : Option < & ' a str > ,
37
38
platform : Option < & ' a Platform > ,
@@ -100,12 +101,25 @@ impl<'a> UbiBuilder<'a> {
100
101
/// Set the name of the executable to look for in archive files. By default this is the same as
101
102
/// the project name, so for `houseabsolute/precious` we look for `precious` or
102
103
/// `precious.exe`. When running on Windows the ".exe" suffix will be added as needed.
104
+ ///
105
+ /// You cannot call `extract_all` if you set this.
103
106
#[ must_use]
104
107
pub fn exe ( mut self , exe : & ' a str ) -> Self {
105
108
self . exe = Some ( exe) ;
106
109
self
107
110
}
108
111
112
+ /// Call this to tell `ubi` to extract all files from the archive. By default `ubi` will look
113
+ /// for an executable in an archive file. But if this is true, it will simply unpack the archive
114
+ /// file in the specified directory.
115
+ ///
116
+ /// You cannot set `exe` when this is true.
117
+ #[ must_use]
118
+ pub fn extract_all ( mut self ) -> Self {
119
+ self . extract_all = true ;
120
+ self
121
+ }
122
+
109
123
/// Set a GitHub token to use for API requests. If this is not set then this will be taken from
110
124
/// the `GITHUB_TOKEN` env var if it is set.
111
125
#[ must_use]
@@ -175,6 +189,9 @@ impl<'a> UbiBuilder<'a> {
175
189
if self . url . is_some ( ) && ( self . project . is_some ( ) || self . tag . is_some ( ) ) {
176
190
return Err ( anyhow ! ( "You cannot set a url with a project or tag" ) ) ;
177
191
}
192
+ if self . exe . is_some ( ) && self . extract_all {
193
+ return Err ( anyhow ! ( "You cannot set exe and extract_all" ) ) ;
194
+ }
178
195
179
196
let platform = self . determine_platform ( ) ?;
180
197
@@ -183,20 +200,37 @@ impl<'a> UbiBuilder<'a> {
183
200
let asset_url = self . url . map ( Url :: parse) . transpose ( ) ?;
184
201
let ( project_name, forge_type) =
185
202
parse_project_name ( self . project , asset_url. as_ref ( ) , self . forge . clone ( ) ) ?;
186
- let exe = exe_name ( self . exe , & project_name, & platform) ;
203
+ let installer = self . new_installer ( & project_name, & platform) ? ;
187
204
let forge = self . new_forge ( project_name, & forge_type) ?;
188
- let install_path = install_path ( self . install_dir , & exe) ?;
189
205
let is_musl = self . is_musl . unwrap_or_else ( || platform_is_musl ( & platform) ) ;
190
206
191
207
Ok ( Ubi :: new (
192
208
forge,
193
209
asset_url,
194
- AssetPicker :: new ( self . matching , platform, is_musl) ,
195
- Installer :: new ( install_path , exe ) ,
210
+ AssetPicker :: new ( self . matching , platform, is_musl, self . extract_all ) ,
211
+ installer ,
196
212
reqwest_client ( ) ?,
197
213
) )
198
214
}
199
215
216
+ fn new_installer ( & self , project_name : & str , platform : & Platform ) -> Result < Box < dyn Installer > > {
217
+ let ( install_path, exe) = if self . extract_all {
218
+ ( install_path ( self . install_dir . as_deref ( ) , None ) ?, None )
219
+ } else {
220
+ let exe = exe_name ( self . exe , project_name, platform) ;
221
+ (
222
+ install_path ( self . install_dir . as_deref ( ) , Some ( & exe) ) ?,
223
+ Some ( exe) ,
224
+ )
225
+ } ;
226
+
227
+ Ok ( if self . extract_all {
228
+ Box :: new ( ArchiveInstaller :: new ( install_path) )
229
+ } else {
230
+ Box :: new ( ExeInstaller :: new ( install_path, exe. unwrap ( ) ) )
231
+ } )
232
+ }
233
+
200
234
fn new_forge (
201
235
& self ,
202
236
project_name : String ,
@@ -283,17 +317,19 @@ fn parse_project_name(
283
317
) )
284
318
}
285
319
286
- fn install_path ( install_dir : Option < PathBuf > , exe : & str ) -> Result < PathBuf > {
287
- let mut path = if let Some ( i ) = install_dir {
288
- i
320
+ fn install_path ( install_dir : Option < & Path > , exe : Option < & str > ) -> Result < PathBuf > {
321
+ let mut install_dir = if let Some ( install_dir ) = install_dir {
322
+ install_dir . to_path_buf ( )
289
323
} else {
290
- let mut i = env:: current_dir ( ) ?;
291
- i . push ( "bin" ) ;
292
- i
324
+ let mut install_dir = env:: current_dir ( ) ?;
325
+ install_dir . push ( "bin" ) ;
326
+ install_dir
293
327
} ;
294
- path. push ( exe) ;
295
- debug ! ( "install path = {}" , path. to_string_lossy( ) ) ;
296
- Ok ( path)
328
+ if let Some ( exe) = exe {
329
+ install_dir. push ( exe) ;
330
+ }
331
+ debug ! ( "install path = {}" , install_dir. to_string_lossy( ) ) ;
332
+ Ok ( install_dir)
297
333
}
298
334
299
335
fn exe_name ( exe : Option < & str > , project_name : & str , platform : & Platform ) -> String {
@@ -303,12 +339,13 @@ fn exe_name(exe: Option<&str>, project_name: &str, platform: &Platform) -> Strin
303
339
_ => e. to_string ( ) ,
304
340
}
305
341
} else {
306
- let parts = project_name. split ( '/' ) . collect :: < Vec < & str > > ( ) ;
307
- let e = parts[ parts. len ( ) - 1 ] . to_string ( ) ;
342
+ // We know that this contains a slash because it already went through `parse_project_name`
343
+ // successfully.
344
+ let e = project_name. split ( '/' ) . last ( ) . unwrap ( ) ;
308
345
if matches ! ( platform. target_os, OS :: Windows ) {
309
346
format ! ( "{e}.exe" )
310
347
} else {
311
- e
348
+ e. to_string ( )
312
349
}
313
350
} ;
314
351
debug ! ( "exe name = {name}" ) ;
0 commit comments