soroban_cli/commands/plugin/
default.rs1use std::{path::PathBuf, process::Command};
2use which::which;
3
4use crate::utils;
5
6#[derive(thiserror::Error, Debug)]
7pub enum Error {
8 #[error(transparent)]
9 IO(#[from] std::io::Error),
10
11 #[error(transparent)]
12 Which(#[from] which::Error),
13
14 #[error(transparent)]
15 Regex(#[from] regex::Error),
16}
17
18pub fn run() -> Result<(), Error> {
19 if let Some((plugin_bin, args)) = find_plugin() {
20 std::process::exit(
21 Command::new(plugin_bin)
22 .args(args)
23 .spawn()?
24 .wait()?
25 .code()
26 .unwrap(),
27 );
28 }
29
30 Ok(())
31}
32
33const MAX_HEX_LENGTH: usize = 10;
34
35fn find_bin(name: &str) -> Result<PathBuf, which::Error> {
36 if let Ok(path) = which(format!("stellar-{name}")) {
37 Ok(path)
38 } else {
39 which(format!("soroban-{name}"))
40 }
41}
42
43pub fn list() -> Result<Vec<String>, Error> {
44 let re_str = if cfg!(target_os = "windows") {
45 r"^(soroban|stellar)-.*.exe$"
46 } else {
47 r"^(soroban|stellar)-.*"
48 };
49
50 let re = regex::Regex::new(re_str)?;
51
52 Ok(which::which_re(re)?
53 .filter_map(|b| {
54 let s = b.file_name()?.to_str()?;
55 Some(s.strip_suffix(".exe").unwrap_or(s).to_string())
56 })
57 .filter(|s| !(utils::is_hex_string(s) && s.len() > MAX_HEX_LENGTH))
58 .map(|s| s.replace("soroban-", "").replace("stellar-", ""))
59 .collect())
60}
61
62fn find_plugin() -> Option<(PathBuf, Vec<String>)> {
63 let args_vec: Vec<String> = std::env::args().skip(1).collect();
64 let mut chain: Vec<String> = args_vec
65 .iter()
66 .take_while(|arg| !arg.starts_with("--"))
67 .map(ToString::to_string)
68 .collect();
69
70 while !chain.is_empty() {
71 let name = chain.join("-");
72 let bin = find_bin(&name).ok();
73
74 if let Some(bin) = &bin {
75 let index = chain.len();
76 let args = args_vec[index..]
77 .iter()
78 .map(ToString::to_string)
79 .collect::<Vec<String>>();
80
81 return Some((bin.into(), args));
82 }
83
84 chain.pop();
85 }
86
87 None
88}