use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;
pub const DEFAULT_EPMD_PORT: u16 = 4370;
#[derive(Debug, Error)]
pub enum EpmdError {
#[error("Connection failed: {0}")]
ConnectionFailed(String),
#[error("Node not found: {0}")]
NodeNotFound(String),
#[error("Node already registered: {0}")]
NodeAlreadyRegistered(String),
#[error("Protocol error: {0}")]
ProtocolError(String),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("Serialization error: {0}")]
SerializationError(String),
}
pub type Result<T> = std::result::Result<T, EpmdError>;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct NodeInfo {
pub name: String,
pub host: String,
pub port: u16,
pub metadata: NodeMetadata,
}
impl NodeInfo {
pub fn new(name: String, host: String, port: u16) -> Self {
Self {
name,
host,
port,
metadata: NodeMetadata::default(),
}
}
pub fn with_metadata(name: String, host: String, port: u16, metadata: NodeMetadata) -> Self {
Self {
name,
host,
port,
metadata,
}
}
pub fn address(&self) -> String {
format!("{}:{}", self.host, self.port)
}
}
impl fmt::Display for NodeInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}@{}:{}", self.name, self.host, self.port)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct NodeMetadata {
pub protocol_version: u32,
pub capabilities: Vec<String>,
pub attributes: std::collections::HashMap<String, String>,
}
impl Default for NodeMetadata {
fn default() -> Self {
Self {
protocol_version: 1,
capabilities: vec![],
attributes: std::collections::HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EpmdMessage {
Register {
name: String,
host: String,
port: u16,
metadata: NodeMetadata,
},
Unregister { name: String },
Lookup { name: String },
ListNodes,
Ping,
RegisterOk,
RegisterError { reason: String },
UnregisterOk,
LookupResult { node: Option<NodeInfo> },
NodeList { nodes: Vec<NodeInfo> },
Pong,
KeepAlive { name: String },
KeepAliveAck,
}
impl EpmdMessage {
pub fn to_bytes(&self) -> Result<Vec<u8>> {
bincode::serialize(self).map_err(|e| EpmdError::SerializationError(e.to_string()))
}
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
bincode::deserialize(bytes).map_err(|e| EpmdError::SerializationError(e.to_string()))
}
}
mod client;
mod server;
pub use client::EpmdClient;
pub use server::EpmdServer;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_info_creation() {
let info = NodeInfo::new("test_node".to_string(), "127.0.0.1".to_string(), 5000);
assert_eq!(info.name, "test_node");
assert_eq!(info.host, "127.0.0.1");
assert_eq!(info.port, 5000);
assert_eq!(info.address(), "127.0.0.1:5000");
}
#[test]
fn test_message_serialization() {
let msg = EpmdMessage::Register {
name: "test".to_string(),
host: "localhost".to_string(),
port: 5000,
metadata: NodeMetadata::default(),
};
let bytes = msg.to_bytes().unwrap();
let deserialized = EpmdMessage::from_bytes(&bytes).unwrap();
match deserialized {
EpmdMessage::Register { name, .. } => assert_eq!(name, "test"),
_ => panic!("Wrong message type"),
}
}
#[test]
fn test_node_info_display() {
let info = NodeInfo::new("node1".to_string(), "192.168.1.100".to_string(), 4000);
assert_eq!(info.to_string(), "node1@192.168.1.100:4000");
}
}