use crate::bech32::auto_bech32_to_hex;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
#[derive(Error, Debug, Eq, PartialEq)]
pub enum NIP5Error {
#[error("NIP05 must be at the format username@domain or _@domain")]
InvalidFormat,
#[error("NIP05 response is in an invalid format")]
InvalidResponseFormat,
#[error("Can't be accessed / Request failed")]
RequestFailed,
#[error("Public key doesn't match with the given NIP05 identifier")]
MatchFailed,
#[error("Bech32 Error: {}", _0)]
Bech32Error(#[from] crate::bech32::Bech32Error),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NostrWellKnown {
pub names: HashMap<String, String>,
}
#[cfg(not(feature = "async"))]
pub fn check_validity(nip05: &str, pubkey: &str) -> Result<bool, NIP5Error> {
let hex_pubkey = auto_bech32_to_hex(pubkey)?;
let pubkey_found = get_nip05(nip05)?;
Ok(pubkey_found == hex_pubkey)
}
#[cfg(feature = "async")]
pub async fn check_validity(nip05: &str, pubkey: &str) -> Result<bool, NIP5Error> {
let hex_pubkey = auto_bech32_to_hex(pubkey)?;
let pubkey_found = get_nip05(nip05).await?;
Ok(pubkey_found == hex_pubkey)
}
#[cfg(not(feature = "async"))]
pub fn get_nips05(domain: &str) -> Result<NostrWellKnown, NIP5Error> {
let relay_response: NostrWellKnown = match reqwest::blocking::Client::new()
.get(format!("https://{domain}/.well-known/nostr.json"))
.send()
{
Ok(response) => match response.json() {
Ok(json) => json,
Err(_) => return Err(NIP5Error::InvalidResponseFormat),
},
Err(_) => return Err(NIP5Error::RequestFailed),
};
Ok(relay_response)
}
#[cfg(feature = "async")]
pub async fn get_nips05(domain: &str) -> Result<NostrWellKnown, NIP5Error> {
let relay_response: NostrWellKnown = match reqwest::Client::new()
.get(format!("https://{domain}/.well-known/nostr.json"))
.send()
.await
{
Ok(response) => match response.json().await {
Ok(json) => json,
Err(_) => return Err(NIP5Error::InvalidResponseFormat),
},
Err(_) => return Err(NIP5Error::RequestFailed),
};
Ok(relay_response)
}
#[cfg(not(feature = "async"))]
pub fn get_nip05(nip05: &str) -> Result<String, NIP5Error> {
let parts: Vec<&str> = nip05.split('@').collect();
if parts.len() != 2 {
return Err(NIP5Error::InvalidFormat);
}
let list = get_nips05(parts[1])?;
let pubkey = list.names.get(parts[0]);
if let Some(pubkey) = pubkey {
Ok(pubkey.clone())
} else {
Err(NIP5Error::MatchFailed)
}
}
#[cfg(feature = "async")]
pub async fn get_nip05(nip05: &str) -> Result<String, NIP5Error> {
let parts: Vec<&str> = nip05.split('@').collect();
if parts.len() != 2 {
return Err(NIP5Error::InvalidFormat);
}
let list = get_nips05(parts[1]).await?;
let pubkey = list.names.get(parts[0]);
if let Some(pubkey) = pubkey {
Ok(pubkey.clone())
} else {
Err(NIP5Error::MatchFailed)
}
}