use iroh::EndpointAddr;
use iroh_tickets::endpoint::EndpointTicket;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;
#[derive(Debug, thiserror::Error)]
pub enum TicketError {
#[error("invalid connection ticket format")]
InvalidFormat,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Ticket {
pub(crate) inner: EndpointTicket,
}
impl Serialize for Ticket {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Ticket {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl Ticket {
pub fn new(addr: EndpointAddr) -> Self {
Self {
inner: EndpointTicket::new(addr),
}
}
pub fn to_addr(&self) -> EndpointAddr {
self.inner.endpoint_addr().clone()
}
}
impl From<Ticket> for String {
fn from(ticket: Ticket) -> Self {
ticket.to_string()
}
}
impl fmt::Display for Ticket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.inner)
}
}
impl FromStr for Ticket {
type Err = TicketError;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let s = s.trim();
if let Ok(endpoint_ticket) = s.parse::<EndpointTicket>() {
return Ok(Self {
inner: endpoint_ticket,
});
}
if s.starts_with('{') {
if let Ok(addr) = serde_json::from_str::<EndpointAddr>(s) {
return Ok(Self::new(addr));
}
}
Err(TicketError::InvalidFormat)
}
}
impl TryFrom<&str> for Ticket {
type Error = TicketError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.parse()
}
}
impl TryFrom<String> for Ticket {
type Error = TicketError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}