use serde::{Deserialize, Serialize};
use starlang_atom::Atom;
use std::fmt;
use std::net::SocketAddr;
use std::sync::OnceLock;
pub const LOCAL_NODE_ID: u32 = 0;
static LOCAL_NODE_ATOM: OnceLock<Atom> = OnceLock::new();
static THIS_NODE: OnceLock<NodeIdentity> = OnceLock::new();
fn local_node_atom() -> Atom {
*LOCAL_NODE_ATOM.get_or_init(|| Atom::new(""))
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NodeId(u32);
impl NodeId {
#[inline]
pub const fn new(id: u32) -> Self {
Self(id)
}
#[inline]
pub const fn local() -> Self {
Self(LOCAL_NODE_ID)
}
#[inline]
pub const fn as_u32(&self) -> u32 {
self.0
}
#[inline]
pub const fn is_local(&self) -> bool {
self.0 == LOCAL_NODE_ID
}
}
impl From<u32> for NodeId {
fn from(id: u32) -> Self {
Self(id)
}
}
impl From<NodeId> for u32 {
fn from(id: NodeId) -> Self {
id.0
}
}
impl fmt::Debug for NodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeId({})", self.0)
}
}
impl fmt::Display for NodeId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NodeName(String);
impl NodeName {
pub fn new(name: impl Into<String>) -> Self {
Self(name.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
pub fn short_name(&self) -> &str {
self.0.split('@').next().unwrap_or(&self.0)
}
pub fn host(&self) -> &str {
self.0.split('@').nth(1).unwrap_or("")
}
}
impl fmt::Debug for NodeName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "NodeName({:?})", self.0)
}
}
impl fmt::Display for NodeName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<&str> for NodeName {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for NodeName {
fn from(s: String) -> Self {
Self::new(s)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NodeInfo {
pub name: NodeName,
pub id: NodeId,
pub addr: Option<SocketAddr>,
pub creation: u32,
}
impl NodeInfo {
pub fn new(
name: impl Into<NodeName>,
id: NodeId,
addr: Option<SocketAddr>,
creation: u32,
) -> Self {
Self {
name: name.into(),
id,
addr,
creation,
}
}
}
#[derive(Clone, Debug)]
pub struct NodeIdentity {
pub name: NodeName,
pub name_atom: Atom,
pub creation: u32,
}
pub fn init_node(name: NodeName, creation: u32) -> Result<(), NodeIdentity> {
let name_atom = Atom::new(name.as_str());
THIS_NODE.set(NodeIdentity {
name,
name_atom,
creation,
})
}
pub fn node_name() -> Option<&'static NodeName> {
THIS_NODE.get().map(|n| &n.name)
}
pub fn node_name_atom() -> Atom {
THIS_NODE
.get()
.map(|n| n.name_atom)
.unwrap_or_else(local_node_atom)
}
pub fn node_creation() -> u32 {
THIS_NODE.get().map(|n| n.creation).unwrap_or(0)
}
pub fn is_distributed() -> bool {
THIS_NODE.get().is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_id_local() {
let local = NodeId::local();
assert!(local.is_local());
assert_eq!(local.as_u32(), 0);
}
#[test]
fn test_node_id_remote() {
let remote = NodeId::new(5);
assert!(!remote.is_local());
assert_eq!(remote.as_u32(), 5);
}
#[test]
fn test_node_name_parsing() {
let name = NodeName::new("mynode@example.com");
assert_eq!(name.short_name(), "mynode");
assert_eq!(name.host(), "example.com");
}
#[test]
fn test_node_name_no_at() {
let name = NodeName::new("standalone");
assert_eq!(name.short_name(), "standalone");
assert_eq!(name.host(), "");
}
#[test]
fn test_node_id_serialization() {
let id = NodeId::new(42);
let bytes = postcard::to_allocvec(&id).unwrap();
let decoded: NodeId = postcard::from_bytes(&bytes).unwrap();
assert_eq!(id, decoded);
}
}