spl_forge/commands/
config.rs1use crate::cli::{ConfigArgs, ConfigCommand};
2use crate::common::paths::{get_solana_keypair_path, path};
3use crate::common::theme;
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::path::PathBuf;
8
9#[derive(Serialize, Deserialize, Debug, Clone)]
10pub struct ConfigData {
11 pub json_rpc_url: String,
12 pub websocket_url: String,
13 pub keypair_path: String,
14 pub commitment: String,
15 pub address_labels: HashMap<String, String>,
16}
17
18impl ConfigData {
19 pub fn default_values() -> Self {
20 let default_keypair_path = dirs::home_dir()
21 .map(|p| p.join(".config/spl-forge/id.json").to_string_lossy().to_string())
22 .unwrap_or_else(|| "spl-forge-keypair.json".to_string());
23
24 let mut labels = HashMap::new();
25 labels.insert(
26 "11111111111111111111111111111111".to_string(),
27 "System Program".to_string(),
28 );
29
30 Self {
31 json_rpc_url: "https://api.mainnet-beta.solana.com".to_string(),
32 websocket_url: "wss://api.mainnet-beta.solana.com/".to_string(),
33 keypair_path: default_keypair_path,
34 commitment: "confirmed".to_string(),
35 address_labels: labels,
36 }
37 }
38
39 pub fn load() -> Result<Self> {
40 let path = path()?;
41 let config_data = std::fs::read_to_string(path)?;
42 Ok(serde_json::from_str(&config_data)?)
43 }
44
45 pub fn save(&self) -> Result<()> {
46 let path = path()?;
47 let config_data = serde_json::to_string_pretty(self)?;
48 std::fs::write(path, config_data)?;
49 Ok(())
50 }
51}
52
53pub async fn handle_config(args: ConfigArgs) -> Result<()> {
54 match args.command {
55 ConfigCommand::Get => handle_get().await,
56 ConfigCommand::Reset => handle_reset().await,
57 ConfigCommand::Set {
58 network,
59 url,
60 keypair,
61 commitment,
62 } => {
63 handle_set(network, url, keypair, commitment).await
64 }
65 }
66}
67
68fn apply_network_preset(config: &mut ConfigData, network: &str) -> Result<()> {
69 match network.to_ascii_lowercase().as_str() {
70 "devnet" => {
71 config.json_rpc_url = "https://api.devnet.solana.com".to_string();
72 config.websocket_url = "wss://api.devnet.solana.com/".to_string();
73 config.commitment = "confirmed".to_string();
74 }
75 "mainnet" | "mainnet-beta" => {
76 config.json_rpc_url = "https://api.mainnet-beta.solana.com".to_string();
77 config.websocket_url = "wss://api.mainnet-beta.solana.com/".to_string();
78 config.commitment = "confirmed".to_string();
79 }
80 "localhost" | "localnet" => {
81 config.json_rpc_url = "http://127.0.0.1:8899".to_string();
82 config.websocket_url = "ws://127.0.0.1:8900/".to_string();
83 config.commitment = "confirmed".to_string();
84 }
85 _ => {
86 anyhow::bail!(
87 "Invalid network preset '{}'. Use one of: devnet, mainnet, localhost",
88 network
89 );
90 }
91 }
92
93 Ok(())
94}
95
96async fn handle_get() -> Result<()> {
97 let config = ConfigData::load()?;
98 println!();
99 println!("{}", theme::heading("Current Configuration:"));
100 println!(" {:<15} {}", theme::label("Config Path:"), path()?.to_string_lossy());
101 println!(" {:<15} {}", theme::label("RPC URL:"), config.json_rpc_url);
102 println!(" {:<15} {}", theme::label("Websocket URL:"), config.websocket_url);
103 println!(" {:<15} {}", theme::label("Keypair Path:"), config.keypair_path);
104 println!(" {:<15} {}", theme::label("Commitment:"), config.commitment);
105 println!();
106 Ok(())
107}
108
109async fn handle_reset() -> Result<()> {
110 let config = ConfigData::default_values();
111 config.save()?;
112 println!();
113 println!("{}", theme::success("Configuration has been reset to default values."));
114 handle_get().await?; Ok(())
116}
117
118async fn handle_set(
119 network: Option<String>,
120 url: Option<String>,
121 keypair: Option<PathBuf>,
122 commitment: Option<String>,
123) -> Result<()> {
124 if network.is_none() && url.is_none() && keypair.is_none() && commitment.is_none() {
125 println!();
126 println!("{}", theme::warning("No values provided to set. Use --url, --keypair, or --commitment."));
127 println!(
128 "{}",
129 theme::muted(
130 "Examples: spl-forge config set devnet | spl-forge config set localhost | spl-forge config set --url https://api.devnet.solana.com"
131 )
132 );
133 println!();
134 return Ok(());
135 }
136
137 let mut config = ConfigData::load()?;
138
139 if let Some(network) = network {
140 apply_network_preset(&mut config, &network)?;
141 }
142
143 if let Some(url) = url {
144 config.json_rpc_url = url;
145 }
146
147 if let Some(keypair) = keypair {
148 if keypair.to_string_lossy() == "solana-cli" {
149 let solana_keypair_path = get_solana_keypair_path();
150 config.keypair_path = solana_keypair_path.to_string_lossy().to_string();
151 } else {
152 config.keypair_path = keypair.to_string_lossy().to_string();
153 }
154 }
155
156 if let Some(commitment) = commitment {
157 config.commitment = commitment;
158 }
159
160 config.save()?;
161 println!();
162 println!("{}", theme::success("Configuration saved successfully."));
163 handle_get().await?;
164 Ok(())
165}