use std::{fmt, str::FromStr};
use serde::{Deserialize, Serialize};
use crate::{DestinationError, DestinationName, Namespace};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub struct Destination {
namespace: Namespace,
name: DestinationName,
}
impl Destination {
#[must_use]
pub const fn new(namespace: Namespace, name: DestinationName) -> Self {
Self { namespace, name }
}
#[must_use]
pub const fn namespace(&self) -> &Namespace {
&self.namespace
}
#[must_use]
pub const fn name(&self) -> &DestinationName {
&self.name
}
#[must_use]
pub fn canonical_key(&self) -> Vec<u8> {
let namespace = self.namespace.as_str().as_bytes();
let name = self.name.as_str().as_bytes();
let mut key = Vec::with_capacity(4 + namespace.len() + name.len());
key.extend_from_slice(&(namespace.len() as u16).to_be_bytes());
key.extend_from_slice(namespace);
key.extend_from_slice(&(name.len() as u16).to_be_bytes());
key.extend_from_slice(name);
key
}
}
impl FromStr for Destination {
type Err = DestinationError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
let (namespace, name) = value
.split_once('/')
.ok_or(DestinationError::InvalidDestination)?;
if name.contains('/') {
return Err(DestinationError::InvalidDestination);
}
Ok(Self::new(
Namespace::new(namespace)?,
DestinationName::new(name)?,
))
}
}
impl TryFrom<String> for Destination {
type Error = DestinationError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
impl From<Destination> for String {
fn from(value: Destination) -> Self {
value.to_string()
}
}
impl fmt::Display for Destination {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}/{}", self.namespace, self.name)
}
}