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