data_anchor_client/
constants.rs1pub const DEFAULT_CONCURRENCY: usize = 100;
3
4pub const DEFAULT_LOOKBACK_SLOTS: u64 = 100;
7
8const MAINNET_GENESIS_HASH: &str = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvDPxV6zKj1rS1n";
9const DEVNET_GENESIS_HASH: &str = "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG";
10const TESTNET_GENESIS_HASH: &str = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY";
11
12#[derive(thiserror::Error, Debug, PartialEq, Eq)]
14pub enum IndexerUrlError {
15 #[error("Failed to parse indexer URL: {0}")]
17 InvalidUrl(String),
18
19 #[error("Unknown genesis hash: {0}")]
21 UnknownGenesisHash(String),
22
23 #[error("Testnet is not supported")]
25 TestnetNotSupported,
26}
27
28pub type IndexerUrlResult<T = ()> = Result<T, IndexerUrlError>;
30
31#[derive(Debug, Default, Clone, PartialEq, Eq)]
33pub enum IndexerUrl {
34 Staging,
35 #[default]
36 Devnet,
37 Mainnet,
38 Custom(String),
39}
40
41impl IndexerUrl {
42 pub fn from_genesis_hash(genesis_hash: &str) -> IndexerUrlResult<Self> {
44 match genesis_hash {
45 MAINNET_GENESIS_HASH => Ok(IndexerUrl::Mainnet),
46 DEVNET_GENESIS_HASH => Ok(IndexerUrl::Devnet),
47 TESTNET_GENESIS_HASH => Err(IndexerUrlError::TestnetNotSupported),
48 _ => Err(IndexerUrlError::UnknownGenesisHash(
49 genesis_hash.to_string(),
50 )),
51 }
52 }
53
54 pub fn url(&self) -> String {
56 let name = match self {
57 IndexerUrl::Staging => "staging",
58 IndexerUrl::Devnet => "devnet",
59 IndexerUrl::Mainnet => "mainnet",
60 IndexerUrl::Custom(url) => {
61 return url.clone();
62 }
63 };
64 format!("https://{name}.data-anchor.termina.technology")
65 }
66}
67
68impl std::str::FromStr for IndexerUrl {
69 type Err = IndexerUrlError;
70
71 fn from_str(s: &str) -> Result<Self, Self::Err> {
72 if s.is_empty() {
73 return Err(IndexerUrlError::InvalidUrl(
74 "Indexer URL cannot be empty".to_string(),
75 ));
76 }
77 match s {
78 "staging" => Ok(IndexerUrl::Staging),
79 "devnet" => Ok(IndexerUrl::Devnet),
80 "mainnet" => Ok(IndexerUrl::Mainnet),
81 s if s.starts_with("https://") || s.starts_with("http://") => {
82 Ok(IndexerUrl::Custom(s.to_string()))
83 }
84 _ => Err(IndexerUrlError::InvalidUrl(format!(
85 "URL not a valid protocol, expected `http(s)`: {s}"
86 ))),
87 }
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use std::str::FromStr;
94
95 use rstest::rstest;
96
97 use super::*;
98
99 #[rstest]
100 #[case::mainnet(MAINNET_GENESIS_HASH, Ok(IndexerUrl::Mainnet))]
101 #[case::devnet(DEVNET_GENESIS_HASH, Ok(IndexerUrl::Devnet))]
102 #[case::testnet(TESTNET_GENESIS_HASH, Err(IndexerUrlError::TestnetNotSupported))]
103 #[case::unknown("unknown_genesis_hash", Err(IndexerUrlError::UnknownGenesisHash("unknown_genesis_hash".to_string())))]
104 fn test_indexer_url_from_genesis_hash(
105 #[case] genesis_hash: &str,
106 #[case] expected: IndexerUrlResult<IndexerUrl>,
107 ) {
108 assert_eq!(IndexerUrl::from_genesis_hash(genesis_hash), expected);
109 }
110
111 #[rstest]
112 #[case::mainnet(IndexerUrl::Mainnet, "https://mainnet.data-anchor.termina.technology")]
113 #[case::devnet(IndexerUrl::Devnet, "https://devnet.data-anchor.termina.technology")]
114 #[case::staging(IndexerUrl::Staging, "https://staging.data-anchor.termina.technology")]
115 #[case::custom(IndexerUrl::Custom("https://custom.indexer.url".to_string()), "https://custom.indexer.url")]
116 fn test_indexer_url_url(#[case] indexer_url: IndexerUrl, #[case] expected_url: &str) {
117 assert_eq!(indexer_url.url(), expected_url);
118 }
119
120 #[rstest]
121 #[case::valid("https://custom.indexer.url", Ok(IndexerUrl::Custom("https://custom.indexer.url".to_string())))]
122 #[case::staging("staging", Ok(IndexerUrl::Staging))]
123 #[case::devnet("devnet", Ok(IndexerUrl::Devnet))]
124 #[case::mainnet("mainnet", Ok(IndexerUrl::Mainnet))]
125 #[case::invalid("invalid_url", Err(IndexerUrlError::InvalidUrl("URL not a valid protocol, expected `http(s)`: invalid_url".to_string())))]
126 fn test_indexer_url_from_str(
127 #[case] input: &str,
128 #[case] expected: IndexerUrlResult<IndexerUrl>,
129 ) {
130 assert_eq!(IndexerUrl::from_str(input), expected);
131 }
132}