sugar_cli/
parse.rs

1use std::{env, fs::File, path::Path};
2
3use anyhow::{anyhow, Result};
4use console::style;
5use lazy_static::lazy_static;
6use regex::Regex;
7
8use crate::{config::data::*, program_errors::*};
9
10pub fn parse_solana_config() -> Option<SolanaConfig> {
11    let home = if cfg!(unix) {
12        env::var_os("HOME").expect("Couldn't find UNIX home key.")
13    } else if cfg!(windows) {
14        let drive = env::var_os("HOMEDRIVE").expect("Couldn't find Windows home drive key.");
15        let path = env::var_os("HOMEPATH").expect("Couldn't find Windows home path key.");
16        Path::new(&drive).join(path).as_os_str().to_owned()
17    } else if cfg!(target_os = "macos") {
18        env::var_os("HOME").expect("Couldn't find MacOS home key.")
19    } else {
20        panic!("Unsupported OS!");
21    };
22
23    let config_path = Path::new(&home)
24        .join(".config")
25        .join("miraland")
26        .join("cli")
27        .join("config.yml");
28
29    let conf_file = match File::open(config_path) {
30        Ok(f) => f,
31        Err(e) => {
32            println!(
33                "{} {}",
34                style("Failed to open Miraland config file:").bold().red(),
35                style(e).bold().red(),
36            );
37            std::process::exit(1);
38        }
39    };
40    serde_yaml::from_reader(&conf_file).ok()
41}
42
43pub fn path_to_string(path: &Path) -> Result<String> {
44    match path.to_str() {
45        Some(s) => Ok(s.to_string()),
46        None => Err(anyhow!("Couldn't convert path to string.")),
47    }
48}
49
50pub fn parse_sugar_errors(msg: &str) -> String {
51    lazy_static! {
52        static ref RE: Regex =
53            Regex::new(r"(0x[A-Za-z0-9]+)").expect("Failed to compile parse_client_error regex.");
54    }
55
56    let mat = RE.find(msg);
57
58    // If there's an RPC error code match in the message, try to parse it, otherwise return the message back.
59    match mat {
60        Some(m) => {
61            let code = msg[m.start()..m.end()].to_string();
62            find_external_program_error(code)
63        }
64        None => msg.to_owned(),
65    }
66}
67
68fn find_external_program_error(code: String) -> String {
69    let code = code.to_uppercase();
70
71    let parsed_code = if code.contains("0X") {
72        code.replace("0X", "")
73    } else {
74        format!("{:X}", code.parse::<i64>().unwrap())
75    };
76
77    if let Some(e) = ANCHOR_ERROR.get(&parsed_code) {
78        format!("Anchor Error: {e}")
79    } else if let Some(e) = METADATA_ERROR.get(&parsed_code) {
80        format!("Token Metadata Error: {e}")
81    } else {
82        let mut errors = Vec::with_capacity(2);
83
84        if let Some(e) = CANDY_CORE_ERROR.get(&parsed_code) {
85            errors.push(format!("Candy Machine: {e}"));
86        }
87        if let Some(e) = CANDY_GUARD_ERROR.get(&parsed_code) {
88            errors.push(format!("Candy Guard: {e}"));
89        }
90
91        if errors.is_empty() {
92            format!("Unknown error. Code: {code}")
93        } else {
94            let mut message = String::from("Command failed due to the following reason(s):");
95
96            for error in errors {
97                message.push_str(&format!("\n  • {}", error).to_string());
98            }
99
100            message
101        }
102    }
103}