soroban_cli/commands/plugin/
default.rs

1use 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}