cluster.rs 5.1 KB

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