use crate::lnurl::LnUrl;
use crate::Error;
use email_address::EmailAddress;
use serde::{Deserialize, Deserializer, Serialize};
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct LightningAddress {
value: EmailAddress,
}
impl LightningAddress {
pub fn new(value: &str) -> Result<Self, Error> {
EmailAddress::from_str(value)
.map(|value| LightningAddress { value })
.map_err(|_| Error::InvalidLightningAddress)
}
pub fn from_domain_and_local_part(domain: &str, local_part: &str) -> Result<Self, Error> {
let string = format!("{}@{}", local_part, domain);
LightningAddress::new(&string)
}
#[inline]
pub fn lnurlp_url(&self) -> String {
format!(
"https://{}/.well-known/lnurlp/{}",
self.value.domain(),
self.value.local_part()
)
}
#[inline]
pub fn lnurl(&self) -> LnUrl {
LnUrl::from_url(self.lnurlp_url())
}
}
impl FromStr for LightningAddress {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
LightningAddress::new(s)
}
}
impl Serialize for LightningAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.value.as_str())
}
}
impl<'de> Deserialize<'de> for LightningAddress {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
LightningAddress::new(&s).map_err(serde::de::Error::custom)
}
}
impl PartialOrd for LightningAddress {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for LightningAddress {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.value.as_str().cmp(other.value.as_str())
}
}
impl Display for LightningAddress {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value)
}
}
#[cfg(test)]
mod test {
use crate::lightning_address::LightningAddress;
use crate::lnurl::LnUrl;
use std::str::FromStr;
#[test]
fn test_parsing() {
let address = LightningAddress::from_str("ben@opreturnbot.com");
assert!(address.is_ok());
assert_eq!(
address.unwrap().lnurlp_url(),
"https://opreturnbot.com/.well-known/lnurlp/ben"
);
}
#[test]
fn test_invalid_parsing() {
assert!(LightningAddress::from_str("invalid").is_err());
assert!(LightningAddress::from_str("####").is_err());
assert!(LightningAddress::from_str("LNURL1DP68GURN8GHJ7UM9WFMXJCM99E3K7MF0V9CXJ0M385EKVCENXC6R2C35XVUKXEFCV5MKVV34X5EKZD3EV56NYD3HXQURZEPEXEJXXEPNXSCRVWFNV9NXZCN9XQ6XYEFHVGCXXCMYXYMNSERXFQ5FNS").is_err());
}
#[test]
fn test_lnurl() {
let address = LightningAddress::from_str("ben@opreturnbot.com").unwrap();
let lnurl = LnUrl::from_str("lnurl1dp68gurn8ghj7mmswfjhgatjde3x7apwvdhk6tewwajkcmpdddhx7amw9akxuatjd3cz7cn9dc94s6d4").unwrap();
assert_eq!(address.lnurl(), lnurl);
}
}