use std::{fmt::Display, str::FromStr};
use rand::Rng;
use serde::{Deserialize, Serialize};
use super::netlayer::NetLayer;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActorAddress {
proto_id: String,
peer_id: PeerId,
host: String,
}
impl ActorAddress {
pub fn new<N>(host: &str) -> Result<Self, Error>
where
N: NetLayer,
{
let proto_id = N::name();
let mut bytes = [0u8; 32];
let mut rng = rand::rng();
rng.fill(&mut bytes);
Ok(Self {
proto_id: proto_id.to_owned(),
host: host.to_owned(),
peer_id: PeerId::new()?,
})
}
pub fn new_with_peer_id<N>(host: &str, peer_id: PeerId) -> Self
where
N: NetLayer,
{
let proto_id = N::name();
Self {
proto_id: proto_id.to_owned(),
host: host.to_owned(),
peer_id,
}
}
pub fn try_parse(value: &str) -> Result<Self, Error> {
let peer_sep = value.find(':').ok_or(Error::Malformed)?;
let host_sep = value.find('@').ok_or(Error::Malformed)?;
if peer_sep == 0 || host_sep == 0 || peer_sep >= host_sep {
return Err(Error::Malformed);
}
let peer_id = PeerId::try_parse(&value[peer_sep + 1..host_sep])?;
Ok(Self {
proto_id: value[0..peer_sep].to_owned(),
host: value[host_sep + 1..value.len()].to_owned(),
peer_id,
})
}
pub fn proto_id(&self) -> &str {
&self.proto_id
}
pub fn peer_id(&self) -> &PeerId {
&self.peer_id
}
pub fn host(&self) -> &str {
&self.host
}
}
impl FromStr for ActorAddress {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::try_parse(s)
}
}
impl TryFrom<String> for ActorAddress {
type Error = Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::try_parse(&value)
}
}
impl From<ActorAddress> for String {
fn from(value: ActorAddress) -> Self {
value.to_string()
}
}
impl Display for ActorAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}@{}", self.proto_id, self.peer_id, self.host)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct PeerId(Vec<u8>);
impl PeerId {
pub fn new() -> Result<Self, Error> {
let mut rng = rand::rng();
let mut buffer = [0u8; 32];
rng.fill(&mut buffer);
Ok(Self(buffer.to_vec()))
}
pub fn new_from_bytes(bytes: &[u8]) -> Self {
Self(bytes.to_vec())
}
pub fn try_parse(value: &str) -> Result<Self, Error> {
Ok(Self::new_from_bytes(
&base32::decode(base32::Alphabet::Rfc4648Lower { padding: false }, value)
.ok_or(Error::Id)?,
))
}
pub fn bytes(&self) -> &[u8] {
&self.0
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl Display for PeerId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
base32::encode(base32::Alphabet::Rfc4648Lower { padding: false }, &self.0)
)
}
}
#[allow(missing_docs)]
#[derive(Debug)]
pub enum Error {
Malformed,
Id,
}
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Malformed => write!(f, "malformed actor address"),
Error::Id => write!(f, "failed to generate peer ID"),
}
}
}
impl std::error::Error for Error {}
#[cfg(test)]
mod tests {
use crate::actors::remote::netlayer::tcp_layer::TcpNetLayer;
use super::ActorAddress;
#[test]
fn can_generate() {
let addr = ActorAddress::new::<TcpNetLayer>("127.0.0.1").unwrap();
assert_eq!("tcp", addr.proto_id());
assert_eq!("127.0.0.1", addr.host());
assert_eq!(32, addr.peer_id().len());
assert_eq!(52, addr.peer_id().to_string().len());
}
#[test]
fn can_parse() {
let addr_str = "tcp:somethingelse@example.com";
let addr = ActorAddress::try_parse(addr_str).unwrap();
assert_eq!("tcp", addr.proto_id());
assert_eq!("somethingelse", addr.peer_id().to_string());
assert_eq!("example.com", addr.host());
}
#[test]
fn can_parse_host_and_port() {
let addr_str = "tcp:anotherone@example.com:8037";
let addr = ActorAddress::try_parse(addr_str).unwrap();
assert_eq!("tcp", addr.proto_id());
assert_eq!("anotherone", addr.peer_id().to_string());
assert_eq!("example.com:8037", addr.host());
}
#[test]
fn empty_address_fails() {
ActorAddress::try_parse(":@").unwrap_err();
}
#[test]
fn malformed_address_fails() {
ActorAddress::try_parse("jkfd@fdk:asdj").unwrap_err();
}
}