use core::fmt;
#[cfg(not(target_arch = "wasm32"))]
use std::net::SocketAddr;
#[cfg(not(target_arch = "wasm32"))]
use reqwest::Proxy;
use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Debug)]
pub enum Error {
InvalidInformationDocument,
InaccessibleInformationDocument,
InvalidScheme,
Reqwest(reqwest::Error),
}
impl std::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidInformationDocument => {
write!(f, "The relay information document is invalid")
}
Self::InaccessibleInformationDocument => {
write!(f, "The relay information document is not accessible")
}
Self::InvalidScheme => write!(f, "Provided URL scheme is not valid"),
Self::Reqwest(e) => write!(f, "{e}"),
}
}
}
impl From<reqwest::Error> for Error {
fn from(e: reqwest::Error) -> Self {
Self::Reqwest(e)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct RelayInformationDocument {
pub name: Option<String>,
pub description: Option<String>,
pub pubkey: Option<String>,
pub contact: Option<String>,
pub supported_nips: Option<Vec<u16>>,
pub software: Option<String>,
pub version: Option<String>,
}
impl RelayInformationDocument {
pub fn new() -> Self {
Self::default()
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn get(url: Url, proxy: Option<SocketAddr>) -> Result<Self, Error> {
use reqwest::Client;
let mut builder = Client::builder();
if let Some(proxy) = proxy {
let proxy = format!("socks5h://{proxy}");
builder = builder.proxy(Proxy::all(proxy)?);
}
let client: Client = builder.build()?;
let url = Self::with_http_scheme(url)?;
let req = client.get(url).header("Accept", "application/nostr+json");
match req.send().await {
Ok(response) => match response.json().await {
Ok(json) => Ok(json),
Err(_) => Err(Error::InvalidInformationDocument),
},
Err(_) => Err(Error::InaccessibleInformationDocument),
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(feature = "blocking")]
pub fn get_blocking(url: Url, proxy: Option<SocketAddr>) -> Result<Self, Error> {
use reqwest::blocking::Client;
let mut builder = Client::builder();
if let Some(proxy) = proxy {
let proxy = format!("socks5h://{proxy}");
builder = builder.proxy(Proxy::all(proxy)?);
}
let client: Client = builder.build()?;
let url = Self::with_http_scheme(url)?;
let req = client.get(url).header("Accept", "application/nostr+json");
match req.send() {
Ok(response) => match response.json() {
Ok(json) => Ok(json),
Err(_) => Err(Error::InvalidInformationDocument),
},
Err(_) => Err(Error::InaccessibleInformationDocument),
}
}
#[cfg(target_arch = "wasm32")]
pub async fn get(url: Url) -> Result<Self, Error> {
use reqwest::Client;
let client: Client = Client::new();
let url = Self::with_http_scheme(url)?;
let req = client.get(url).header("Accept", "application/nostr+json");
match req.send().await {
Ok(response) => match response.json().await {
Ok(json) => Ok(json),
Err(_) => Err(Error::InvalidInformationDocument),
},
Err(_) => Err(Error::InaccessibleInformationDocument),
}
}
fn with_http_scheme(url: Url) -> Result<Url, Error> {
let mut url = url;
match url.scheme() {
"wss" => url.set_scheme("https").map_err(|_| Error::InvalidScheme)?,
"ws" => url.set_scheme("http").map_err(|_| Error::InvalidScheme)?,
_ => {}
}
Ok(url)
}
}