relaygate_destination/
destination.rs1use std::{fmt, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4
5use crate::{DestinationError, DestinationName, Namespace};
6
7#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9#[serde(try_from = "String", into = "String")]
10pub struct Destination {
11 namespace: Namespace,
12 name: DestinationName,
13}
14
15impl Destination {
16 #[must_use]
18 pub const fn new(namespace: Namespace, name: DestinationName) -> Self {
19 Self { namespace, name }
20 }
21
22 #[must_use]
24 pub const fn namespace(&self) -> &Namespace {
25 &self.namespace
26 }
27
28 #[must_use]
30 pub const fn name(&self) -> &DestinationName {
31 &self.name
32 }
33
34 #[must_use]
39 pub fn canonical_key(&self) -> Vec<u8> {
40 let namespace = self.namespace.as_str().as_bytes();
41 let name = self.name.as_str().as_bytes();
42 let mut key = Vec::with_capacity(4 + namespace.len() + name.len());
43 key.extend_from_slice(&(namespace.len() as u16).to_be_bytes());
44 key.extend_from_slice(namespace);
45 key.extend_from_slice(&(name.len() as u16).to_be_bytes());
46 key.extend_from_slice(name);
47 key
48 }
49}
50
51impl FromStr for Destination {
52 type Err = DestinationError;
53
54 fn from_str(value: &str) -> Result<Self, Self::Err> {
55 let (namespace, name) = value
56 .split_once('/')
57 .ok_or(DestinationError::InvalidDestination)?;
58 if name.contains('/') {
59 return Err(DestinationError::InvalidDestination);
60 }
61 Ok(Self::new(
62 Namespace::new(namespace)?,
63 DestinationName::new(name)?,
64 ))
65 }
66}
67
68impl TryFrom<String> for Destination {
69 type Error = DestinationError;
70
71 fn try_from(value: String) -> Result<Self, Self::Error> {
72 value.parse()
73 }
74}
75
76impl From<Destination> for String {
77 fn from(value: Destination) -> Self {
78 value.to_string()
79 }
80}
81
82impl fmt::Display for Destination {
83 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(formatter, "{}/{}", self.namespace, self.name)
85 }
86}