use crate::resolve::{interpolate, UnresolvedEnvVarError, RE_PLACEHOLDER};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{
collections::BTreeMap,
fmt,
ops::{Deref, DerefMut},
};
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(transparent)]
pub struct RpcEndpoints {
endpoints: BTreeMap<String, RpcEndpoint>,
}
impl RpcEndpoints {
pub fn new(endpoints: impl IntoIterator<Item = (impl Into<String>, RpcEndpoint)>) -> Self {
Self { endpoints: endpoints.into_iter().map(|(name, url)| (name.into(), url)).collect() }
}
pub fn is_empty(&self) -> bool {
self.endpoints.is_empty()
}
pub fn resolved(self) -> ResolvedRpcEndpoints {
ResolvedRpcEndpoints {
endpoints: self.endpoints.into_iter().map(|(name, e)| (name, e.resolve())).collect(),
}
}
}
impl Deref for RpcEndpoints {
type Target = BTreeMap<String, RpcEndpoint>;
fn deref(&self) -> &Self::Target {
&self.endpoints
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RpcEndpoint {
Url(String),
Env(String),
}
impl RpcEndpoint {
pub fn as_url(&self) -> Option<&str> {
match self {
RpcEndpoint::Url(url) => Some(url),
RpcEndpoint::Env(_) => None,
}
}
pub fn as_env(&self) -> Option<&str> {
match self {
RpcEndpoint::Env(val) => Some(val),
RpcEndpoint::Url(_) => None,
}
}
pub fn resolve(self) -> Result<String, UnresolvedEnvVarError> {
match self {
RpcEndpoint::Url(url) => Ok(url),
RpcEndpoint::Env(val) => interpolate(&val),
}
}
}
impl fmt::Display for RpcEndpoint {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RpcEndpoint::Url(url) => url.fmt(f),
RpcEndpoint::Env(var) => var.fmt(f),
}
}
}
impl TryFrom<RpcEndpoint> for String {
type Error = UnresolvedEnvVarError;
fn try_from(value: RpcEndpoint) -> Result<Self, Self::Error> {
value.resolve()
}
}
impl Serialize for RpcEndpoint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for RpcEndpoint {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let val = String::deserialize(deserializer)?;
let endpoint = if RE_PLACEHOLDER.is_match(&val) {
RpcEndpoint::Env(val)
} else {
RpcEndpoint::Url(val)
};
Ok(endpoint)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ResolvedRpcEndpoints {
endpoints: BTreeMap<String, Result<String, UnresolvedEnvVarError>>,
}
impl ResolvedRpcEndpoints {
pub fn has_unresolved(&self) -> bool {
self.endpoints.values().any(|val| val.is_err())
}
}
impl Deref for ResolvedRpcEndpoints {
type Target = BTreeMap<String, Result<String, UnresolvedEnvVarError>>;
fn deref(&self) -> &Self::Target {
&self.endpoints
}
}
impl DerefMut for ResolvedRpcEndpoints {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.endpoints
}
}