plexor-core 0.1.0-alpha.2

Core library for the rust implementation of the Plexo distributed system architecture, providing the fundamental Plexus, Neuron, Codec, and Axon abstractions.
Documentation
// Copyright 2025 Alecks Gates
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::fmt;
use std::sync::Arc;

pub trait Namespace: fmt::Display + Send + Sync + 'static {
    fn path(&self) -> String;
    fn with_suffix(&self, suffix: Vec<&str>) -> String;
}

/// A static implementation of Namespace.
///
/// # Examples
///
/// ```rust
/// # use plexor_core::namespace::{Namespace, NamespaceImpl};
/// #
/// let ns = NamespaceImpl { delimiter: ".", parts: vec!["dev", "plexo"] };
/// assert_eq!(ns.path(), "dev.plexo");
/// assert_eq!(ns.with_suffix(vec!["test"]), "dev.plexo.test");
/// ```
#[derive(Debug, Clone)]
pub struct NamespaceImpl {
    pub delimiter: &'static str,
    pub parts: Vec<&'static str>,
}

impl NamespaceImpl {
    /// Create an Arc<NamespaceImpl> from this instance
    pub fn into_arc(self) -> Arc<NamespaceImpl> {
        Arc::new(self)
    }
}

impl Namespace for NamespaceImpl {
    fn path(&self) -> String {
        self.parts.join(self.delimiter)
    }

    fn with_suffix(&self, suffix: Vec<&str>) -> String {
        let parts_with_suffix: Vec<&str> = self.parts.clone().into_iter().chain(suffix).collect();

        parts_with_suffix.join(self.delimiter)
    }
}

impl fmt::Display for NamespaceImpl {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.path())
    }
}

/// A dynamic version of Namespace for cases where parts are only known at runtime (e.g. discovery).
#[derive(Debug, Clone)]
pub struct DynamicNamespace {
    pub delimiter: String,
    pub parts: Vec<String>,
}

impl DynamicNamespace {
    /// Create a DynamicNamespace from a string with a delimiter.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use plexor_core::namespace::{Namespace, DynamicNamespace};
    /// #
    /// let ns = DynamicNamespace::from_str("dev.plexo", ".");
    /// assert_eq!(ns.path(), "dev.plexo");
    /// assert_eq!(ns.with_suffix(vec!["test"]), "dev.plexo.test");
    /// ```
    pub fn from_str(path: &str, delimiter: &str) -> Self {
        Self {
            delimiter: delimiter.to_string(),
            parts: path.split(delimiter).map(|s| s.to_string()).collect(),
        }
    }

    /// Helper to create an Arc<DynamicNamespace>
    pub fn new_arc(path: &str, delimiter: &str) -> Arc<Self> {
        Arc::new(Self::from_str(path, delimiter))
    }
}

impl Namespace for DynamicNamespace {
    fn path(&self) -> String {
        self.parts.join(&self.delimiter)
    }

    fn with_suffix(&self, suffix: Vec<&str>) -> String {
        let mut p = self.parts.clone();
        p.extend(suffix.iter().map(|s| s.to_string()));
        p.join(&self.delimiter)
    }
}

impl fmt::Display for DynamicNamespace {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.path())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test_utils::test_namespace;

    #[test]
    fn test_namespace_path() {
        let ns = test_namespace();
        assert_eq!("dev.plexo", ns.path());
    }

    #[test]
    fn test_namespace_with_suffix() {
        let ns = test_namespace();
        assert_eq!(
            "dev.plexo.test.foobar",
            ns.with_suffix(vec!["test", "foobar"])
        );
    }

    #[test]
    fn test_namespace_display() {
        let ns = NamespaceImpl {
            delimiter: ".",
            parts: vec!["dev", "plexo"],
        };
        assert_eq!("dev.plexo", format!("{ns}"));
    }
}