1
1
use std:: convert:: Infallible ;
2
+ use std:: path:: { Path , PathBuf } ;
2
3
use std:: {
3
4
ffi:: OsString ,
4
5
process:: { Command , ExitCode , ExitStatus } ,
@@ -22,6 +23,57 @@ fn exec_spawn(cmd: &mut Command) -> std::io::Result<Infallible> {
22
23
}
23
24
}
24
25
26
+ /// Assuming the binary is called something like `[email protected] (.exe)`, compute the `@1.2.3(.exe)` part
27
+ /// so that we can preferentially find `[email protected] (.exe)`, for folks who like managing multiple
28
+ /// installs in this way.
29
+ fn get_uvx_suffix ( current_exe : & Path ) -> Option < & str > {
30
+ let os_file_name = current_exe. file_name ( ) ?;
31
+ let file_name_str = os_file_name. to_str ( ) ?;
32
+ file_name_str. strip_prefix ( "uvx" )
33
+ }
34
+
35
+ /// Gets the path to `uv`, given info about `uvx`
36
+ fn get_uv_path ( current_exe_parent : & Path , uvx_suffix : Option < & str > ) -> std:: io:: Result < PathBuf > {
37
+ // First try to find a matching suffixed `uv`, e.g. `[email protected] (.exe)`
38
+ let uv_with_suffix = uvx_suffix. map ( |suffix| current_exe_parent. join ( format ! ( "uv{suffix}" ) ) ) ;
39
+ if let Some ( uv_with_suffix) = & uv_with_suffix {
40
+ #[ allow( clippy:: print_stderr, reason = "printing a very rare warning" ) ]
41
+ match uv_with_suffix. try_exists ( ) {
42
+ Ok ( true ) => return Ok ( uv_with_suffix. to_owned ( ) ) ,
43
+ Ok ( false ) => { /* definitely not there, proceed to fallback */ }
44
+ Err ( err) => {
45
+ // We don't know if `[email protected] ` exists, something errored when checking.
46
+ // We *could* blindly use `[email protected] ` in this case, as the code below does, however
47
+ // in this extremely narrow corner case it's *probably* better to default to `uv`,
48
+ // since we don't want to mess up existing users who weren't using suffixes?
49
+ eprintln ! (
50
+ "warning: failed to determine if `{}` exists, trying `uv` instead: {err}" ,
51
+ uv_with_suffix. display( )
52
+ ) ;
53
+ }
54
+ }
55
+ }
56
+
57
+ // Then just look for good ol' `uv`
58
+ let uv = current_exe_parent. join ( format ! ( "uv{}" , std:: env:: consts:: EXE_SUFFIX ) ) ;
59
+ // If we are sure the `uv` binary does not exist, display a clearer error message.
60
+ // If we're not certain if uv exists (try_exists == Err), keep going and hope it works.
61
+ if matches ! ( uv. try_exists( ) , Ok ( false ) ) {
62
+ let message = if let Some ( uv_with_suffix) = uv_with_suffix {
63
+ format ! (
64
+ "Could not find the `uv` binary at either of:\n {}\n {}" ,
65
+ uv_with_suffix. display( ) ,
66
+ uv. display( ) ,
67
+ )
68
+ } else {
69
+ format ! ( "Could not find the `uv` binary at: {}" , uv. display( ) )
70
+ } ;
71
+ Err ( std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound , message) )
72
+ } else {
73
+ Ok ( uv)
74
+ }
75
+ }
76
+
25
77
fn run ( ) -> std:: io:: Result < ExitStatus > {
26
78
let current_exe = std:: env:: current_exe ( ) ?;
27
79
let Some ( bin) = current_exe. parent ( ) else {
@@ -30,22 +82,15 @@ fn run() -> std::io::Result<ExitStatus> {
30
82
"Could not determine the location of the `uvx` binary" ,
31
83
) ) ;
32
84
} ;
33
- let uv = bin. join ( format ! ( "uv{}" , std:: env:: consts:: EXE_SUFFIX ) ) ;
85
+ let uvx_suffix = get_uvx_suffix ( & current_exe) ;
86
+ let uv = get_uv_path ( bin, uvx_suffix) ?;
34
87
let args = [ "tool" , "uvx" ]
35
88
. iter ( )
36
89
. map ( OsString :: from)
37
90
// Skip the `uvx` name
38
91
. chain ( std:: env:: args_os ( ) . skip ( 1 ) )
39
92
. collect :: < Vec < _ > > ( ) ;
40
93
41
- // If we are sure the uv binary does not exist, display a clearer error message
42
- if matches ! ( uv. try_exists( ) , Ok ( false ) ) {
43
- return Err ( std:: io:: Error :: new (
44
- std:: io:: ErrorKind :: NotFound ,
45
- format ! ( "Could not find the `uv` binary at: {}" , uv. display( ) ) ,
46
- ) ) ;
47
- }
48
-
49
94
let mut cmd = Command :: new ( uv) ;
50
95
cmd. args ( & args) ;
51
96
match exec_spawn ( & mut cmd) ? { }
0 commit comments