gemachain_cli_config/
config.rs1use serde_derive::{Deserialize, Serialize};
3use std::{collections::HashMap, io, path::Path};
4use url::Url;
5
6lazy_static! {
7 pub static ref CONFIG_FILE: Option<String> = {
8 dirs_next::home_dir().map(|mut path| {
9 path.extend(&[".config", "gemachain", "cli", "config.yml"]);
10 path.to_str().unwrap().to_string()
11 })
12 };
13}
14
15#[derive(Serialize, Deserialize, Debug, PartialEq)]
16pub struct Config {
17 pub json_rpc_url: String,
18 pub websocket_url: String,
19 pub keypair_path: String,
20 #[serde(default)]
21 pub address_labels: HashMap<String, String>,
22 #[serde(default)]
23 pub commitment: String,
24}
25
26impl Default for Config {
27 fn default() -> Self {
28 let keypair_path = {
29 let mut keypair_path = dirs_next::home_dir().expect("home directory");
30 keypair_path.extend(&[".config", "gemachain", "id.json"]);
31 keypair_path.to_str().unwrap().to_string()
32 };
33 let json_rpc_url = "https://api.mainnet-beta.gemachain.com".to_string();
34
35 let websocket_url = "".to_string();
38
39 let mut address_labels = HashMap::new();
40 address_labels.insert(
41 "11111111111111111111111111111111".to_string(),
42 "System Program".to_string(),
43 );
44
45 let commitment = "confirmed".to_string();
46
47 Self {
48 json_rpc_url,
49 websocket_url,
50 keypair_path,
51 address_labels,
52 commitment,
53 }
54 }
55}
56
57impl Config {
58 pub fn load(config_file: &str) -> Result<Self, io::Error> {
59 crate::load_config_file(config_file)
60 }
61
62 pub fn save(&self, config_file: &str) -> Result<(), io::Error> {
63 crate::save_config_file(self, config_file)
64 }
65
66 pub fn compute_websocket_url(json_rpc_url: &str) -> String {
67 let json_rpc_url: Option<Url> = json_rpc_url.parse().ok();
68 if json_rpc_url.is_none() {
69 return "".to_string();
70 }
71 let json_rpc_url = json_rpc_url.unwrap();
72 let is_secure = json_rpc_url.scheme().to_ascii_lowercase() == "https";
73 let mut ws_url = json_rpc_url.clone();
74 ws_url
75 .set_scheme(if is_secure { "wss" } else { "ws" })
76 .expect("unable to set scheme");
77 if let Some(port) = json_rpc_url.port() {
78 let port = port.checked_add(1).expect("port out of range");
79 ws_url.set_port(Some(port)).expect("unable to set port");
80 }
81 ws_url.to_string()
82 }
83
84 pub fn import_address_labels<P>(&mut self, filename: P) -> Result<(), io::Error>
85 where
86 P: AsRef<Path>,
87 {
88 let imports: HashMap<String, String> = crate::load_config_file(filename)?;
89 for (address, label) in imports.into_iter() {
90 self.address_labels.insert(address, label);
91 }
92 Ok(())
93 }
94
95 pub fn export_address_labels<P>(&self, filename: P) -> Result<(), io::Error>
96 where
97 P: AsRef<Path>,
98 {
99 crate::save_config_file(&self.address_labels, filename)
100 }
101}
102
103#[cfg(test)]
104mod test {
105 use super::*;
106
107 #[test]
108 fn compute_websocket_url() {
109 assert_eq!(
110 Config::compute_websocket_url("http://api.devnet.gemachain.com"),
111 "ws://api.devnet.gemachain.com/".to_string()
112 );
113
114 assert_eq!(
115 Config::compute_websocket_url("https://api.devnet.gemachain.com"),
116 "wss://api.devnet.gemachain.com/".to_string()
117 );
118
119 assert_eq!(
120 Config::compute_websocket_url("http://example.com:8899"),
121 "ws://example.com:8900/".to_string()
122 );
123 assert_eq!(
124 Config::compute_websocket_url("https://example.com:1234"),
125 "wss://example.com:1235/".to_string()
126 );
127
128 assert_eq!(Config::compute_websocket_url("garbage"), String::new());
129 }
130}