cluster.rs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. use anyhow::{anyhow, Result};
  2. use serde::{Deserialize, Serialize};
  3. use std::str::FromStr;
  4. use url::Url;
  5. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
  6. pub enum Cluster {
  7. Testnet,
  8. Mainnet,
  9. Devnet,
  10. Localnet,
  11. Debug,
  12. Custom(String, String),
  13. }
  14. impl Default for Cluster {
  15. fn default() -> Self {
  16. Cluster::Localnet
  17. }
  18. }
  19. impl FromStr for Cluster {
  20. type Err = anyhow::Error;
  21. fn from_str(s: &str) -> Result<Cluster> {
  22. match s.to_lowercase().as_str() {
  23. "t" | "testnet" => Ok(Cluster::Testnet),
  24. "m" | "mainnet" => Ok(Cluster::Mainnet),
  25. "d" | "devnet" => Ok(Cluster::Devnet),
  26. "l" | "localnet" => Ok(Cluster::Localnet),
  27. "g" | "debug" => Ok(Cluster::Debug),
  28. _ if s.starts_with("http") => {
  29. let http_url = s;
  30. // Taken from:
  31. // https://github.com/solana-labs/solana/blob/aea8f0df1610248d29d8ca3bc0d60e9fabc99e31/web3.js/src/util/url.ts
  32. let mut ws_url = Url::parse(http_url)?;
  33. if let Some(port) = ws_url.port() {
  34. ws_url.set_port(Some(port + 1))
  35. .map_err(|_| anyhow!("Unable to set port"))?;
  36. }
  37. if ws_url.scheme() == "https" {
  38. ws_url.set_scheme("wss")
  39. .map_err(|_| anyhow!("Unable to set scheme"))?;
  40. } else {
  41. ws_url.set_scheme("ws")
  42. .map_err(|_| anyhow!("Unable to set scheme"))?;
  43. }
  44. Ok(Cluster::Custom(http_url.to_string(), ws_url.to_string()))
  45. }
  46. _ => Err(anyhow::Error::msg(
  47. "Cluster must be one of [localnet, testnet, mainnet, devnet] or be an http or https url\n",
  48. )),
  49. }
  50. }
  51. }
  52. impl std::fmt::Display for Cluster {
  53. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  54. let clust_str = match self {
  55. Cluster::Testnet => "testnet",
  56. Cluster::Mainnet => "mainnet",
  57. Cluster::Devnet => "devnet",
  58. Cluster::Localnet => "localnet",
  59. Cluster::Debug => "debug",
  60. Cluster::Custom(url, _ws_url) => url,
  61. };
  62. write!(f, "{}", clust_str)
  63. }
  64. }
  65. impl Cluster {
  66. pub fn url(&self) -> &str {
  67. match self {
  68. Cluster::Devnet => "https://api.devnet.solana.com",
  69. Cluster::Testnet => "https://api.testnet.solana.com",
  70. Cluster::Mainnet => "https://api.mainnet-beta.solana.com",
  71. Cluster::Localnet => "http://127.0.0.1:8899",
  72. Cluster::Debug => "http://34.90.18.145:8899",
  73. Cluster::Custom(url, _ws_url) => url,
  74. }
  75. }
  76. pub fn ws_url(&self) -> &str {
  77. match self {
  78. Cluster::Devnet => "wss://api.devnet.solana.com",
  79. Cluster::Testnet => "wss://api.testnet.solana.com",
  80. Cluster::Mainnet => "wss://api.mainnet-beta.solana.com",
  81. Cluster::Localnet => "ws://127.0.0.1:9000",
  82. Cluster::Debug => "ws://34.90.18.145:9000",
  83. Cluster::Custom(_url, ws_url) => ws_url,
  84. }
  85. }
  86. }
  87. #[cfg(test)]
  88. mod tests {
  89. use super::*;
  90. fn test_cluster(name: &str, cluster: Cluster) {
  91. assert_eq!(Cluster::from_str(name).unwrap(), cluster);
  92. }
  93. #[test]
  94. fn test_cluster_parse() {
  95. test_cluster("testnet", Cluster::Testnet);
  96. test_cluster("mainnet", Cluster::Mainnet);
  97. test_cluster("devnet", Cluster::Devnet);
  98. test_cluster("localnet", Cluster::Localnet);
  99. test_cluster("debug", Cluster::Debug);
  100. }
  101. #[test]
  102. #[should_panic]
  103. fn test_cluster_bad_parse() {
  104. let bad_url = "httq://my_custom_url.test.net";
  105. Cluster::from_str(bad_url).unwrap();
  106. }
  107. #[test]
  108. fn test_http_port() {
  109. let url = "http://my-url.com:7000/";
  110. let cluster = Cluster::from_str(url).unwrap();
  111. assert_eq!(
  112. Cluster::Custom(url.to_string(), "ws://my-url.com:7001/".to_string()),
  113. cluster
  114. );
  115. }
  116. #[test]
  117. fn test_http_no_port() {
  118. let url = "http://my-url.com/";
  119. let cluster = Cluster::from_str(url).unwrap();
  120. assert_eq!(
  121. Cluster::Custom(url.to_string(), "ws://my-url.com/".to_string()),
  122. cluster
  123. );
  124. }
  125. #[test]
  126. fn test_https_port() {
  127. let url = "https://my-url.com:7000/";
  128. let cluster = Cluster::from_str(url).unwrap();
  129. assert_eq!(
  130. Cluster::Custom(url.to_string(), "wss://my-url.com:7001/".to_string()),
  131. cluster
  132. );
  133. }
  134. #[test]
  135. fn test_https_no_port() {
  136. let url = "https://my-url.com/";
  137. let cluster = Cluster::from_str(url).unwrap();
  138. assert_eq!(
  139. Cluster::Custom(url.to_string(), "wss://my-url.com/".to_string()),
  140. cluster
  141. );
  142. }
  143. #[test]
  144. fn test_upper_case() {
  145. let url = "http://my-url.com/FooBar";
  146. let cluster = Cluster::from_str(url).unwrap();
  147. assert_eq!(
  148. Cluster::Custom(url.to_string(), "ws://my-url.com/FooBar".to_string()),
  149. cluster
  150. );
  151. }
  152. }