soroban_cli/commands/plugin/
default.rs

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