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/.

//! Type-erased versions of neuron traits.

use crate::codec::{Codec, CodecName};
use crate::neuron::{Neuron, NeuronError};
use std::any::{Any, TypeId};
use std::marker::PhantomData;
use std::sync::Arc;

/// Type-erased neuron that can be stored in collections with other type-erased neurons
pub trait NeuronErased: Send + Sync + 'static {
    fn name(&self) -> String;
    fn name_without_codec(&self) -> String;
    fn schema(&self) -> String;
    fn payload_type_id(&self) -> TypeId;
    fn codec_type_id(&self) -> TypeId;
    fn clone_to_box(&self) -> Box<dyn NeuronErased + Send + Sync + 'static>;
    fn clone_to_arc(&self) -> Arc<dyn NeuronErased + Send + Sync + 'static>;
    fn encode_any(&self, data: &dyn std::any::Any) -> Result<Vec<u8>, NeuronError>;
    fn decode_any(&self, data: &[u8]) -> Result<Box<dyn std::any::Any + Send>, NeuronError>;
    fn as_any(&self) -> &dyn Any;
}

/// Wrapper that implements NeuronErased for any concrete Neuron
pub struct NeuronErasedWrapper<T: 'static, C: 'static> {
    neuron: Arc<dyn Neuron<T, C> + Send + Sync + 'static>,
    _phantom: PhantomData<(T, C)>,
}

impl<T, C> NeuronErasedWrapper<T, C>
where
    T: Send + Sync + 'static,
    C: Codec<T> + CodecName + Send + Sync + 'static,
{
    pub fn new(neuron: Arc<dyn Neuron<T, C> + Send + Sync + 'static>) -> Self {
        Self {
            neuron,
            _phantom: PhantomData,
        }
    }

    /// Create a type-erased neuron from a correctly typed neuron
    pub fn from_typed_neuron(
        neuron: Arc<dyn Neuron<T, C> + Send + Sync + 'static>,
    ) -> Arc<dyn NeuronErased + Send + Sync + 'static> {
        Arc::new(Self::new(neuron))
    }

    /// Get the underlying typed neuron
    pub fn get_typed_neuron(&self) -> Arc<dyn Neuron<T, C> + Send + Sync + 'static> {
        self.neuron.clone()
    }
}

impl<T, C> NeuronErased for NeuronErasedWrapper<T, C>
where
    T: Send + Sync + 'static,
    C: Codec<T> + CodecName + Send + Sync + 'static,
{
    fn name(&self) -> String {
        self.neuron.name()
    }

    fn name_without_codec(&self) -> String {
        self.neuron.name_without_codec()
    }

    fn schema(&self) -> String {
        self.neuron.schema()
    }

    fn payload_type_id(&self) -> TypeId {
        TypeId::of::<T>()
    }

    fn codec_type_id(&self) -> TypeId {
        TypeId::of::<C>()
    }

    fn clone_to_box(&self) -> Box<dyn NeuronErased + Send + Sync + 'static> {
        Box::new(NeuronErasedWrapper {
            neuron: self.neuron.clone(),
            _phantom: PhantomData,
        })
    }

    fn clone_to_arc(&self) -> Arc<dyn NeuronErased + Send + Sync + 'static> {
        Arc::new(NeuronErasedWrapper {
            neuron: self.neuron.clone(),
            _phantom: PhantomData,
        })
    }

    fn encode_any(&self, data: &dyn std::any::Any) -> Result<Vec<u8>, NeuronError> {
        if let Some(typed_data) = data.downcast_ref::<T>() {
            self.neuron.encode(typed_data)
        } else {
            Err(NeuronError::Encode {
                neuron_name: self.name(),
                message: "Type mismatch in encode_any".to_string(),
            })
        }
    }

    fn decode_any(&self, data: &[u8]) -> Result<Box<dyn std::any::Any + Send>, NeuronError> {
        let decoded = self.neuron.decode(data)?;
        Ok(Box::new(decoded) as Box<dyn std::any::Any + Send>)
    }

    fn as_any(&self) -> &dyn Any {
        self
    }
}

impl<T, C> Clone for NeuronErasedWrapper<T, C>
where
    T: Send + Sync + 'static,
    C: Codec<T> + CodecName + Send + Sync + 'static,
{
    fn clone(&self) -> Self {
        Self {
            neuron: self.neuron.clone(),
            _phantom: PhantomData,
        }
    }
}

/// Convenience function to create a type-erased neuron from a correctly typed neuron
pub fn erase_neuron<T, C>(
    neuron: Arc<dyn Neuron<T, C> + Send + Sync + 'static>,
) -> Arc<dyn NeuronErased + Send + Sync + 'static>
where
    T: Send + Sync + 'static,
    C: Codec<T> + CodecName + Send + Sync + 'static,
{
    NeuronErasedWrapper::from_typed_neuron(neuron)
}