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