use std::borrow::Cow;
use std::ops::Deref;
use std::time::Duration;
use once_cell::sync::OnceCell;
use tonic::transport::{
Channel,
ClientTlsConfig,
Endpoint,
};
use triomphe::Arc;
use crate::ArcSwap;
pub(crate) const MAINNET: &str = "mainnet-public.mirrornode.hedera.com:443";
pub(crate) const TESTNET: &str = "testnet.mirrornode.hedera.com:443";
pub(crate) const PREVIEWNET: &str = "previewnet.mirrornode.hedera.com:443";
#[derive(Default)]
pub(crate) struct MirrorNetwork(ArcSwap<MirrorNetworkData>);
impl Deref for MirrorNetwork {
type Target = ArcSwap<MirrorNetworkData>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl MirrorNetwork {
pub(crate) fn mainnet() -> Self {
Self::network(MAINNET)
}
pub(crate) fn testnet() -> Self {
Self::network(TESTNET)
}
pub(crate) fn previewnet() -> Self {
Self::network(PREVIEWNET)
}
fn network(address: &'static str) -> Self {
let tls_config = ClientTlsConfig::new().domain_name(address.split_once(':').unwrap().0);
Self(ArcSwap::new(Arc::new(MirrorNetworkData::from_static(&[address], tls_config))))
}
#[cfg(feature = "serde")]
pub(crate) fn from_addresses(addresses: Vec<Cow<'static, str>>) -> Self {
Self(ArcSwap::new(Arc::new(MirrorNetworkData::from_addresses(addresses))))
}
}
#[derive(Clone, Default)]
pub(crate) struct MirrorNetworkData {
addresses: Vec<Cow<'static, str>>,
channel: OnceCell<Channel>,
tls_config: ClientTlsConfig,
}
impl MirrorNetworkData {
pub(crate) fn from_addresses(addresses: Vec<Cow<'static, str>>) -> Self {
Self { addresses, channel: OnceCell::new(), tls_config: ClientTlsConfig::new() }
}
pub(crate) fn from_static(network: &[&'static str], tls_config: ClientTlsConfig) -> Self {
let mut addresses = Vec::with_capacity(network.len());
for address in network {
addresses.push(Cow::Borrowed(*address));
}
Self { addresses, channel: OnceCell::new(), tls_config }
}
pub(crate) fn channel(&self) -> Channel {
self.channel
.get_or_init(|| {
let endpoints = self.addresses.iter().map(|address| {
let uri = format!("tcp://{address}");
Endpoint::from_shared(uri)
.unwrap()
.keep_alive_timeout(Duration::from_secs(10))
.tls_config(self.tls_config.clone())
.unwrap()
.keep_alive_while_idle(true)
.tcp_keepalive(Some(Duration::from_secs(10)))
.connect_timeout(Duration::from_secs(10))
});
Channel::balance_list(endpoints)
})
.clone()
}
pub(crate) fn addresses(&self) -> impl Iterator<Item = String> + '_ {
self.addresses.iter().cloned().map(Cow::into_owned)
}
}