mechtron 0.3.2

Part of http://thecosmicinitiative.io This package--MECHTRON--provides an API and framework that allows a Wasm component to send and receive messages from other Wasm components.
Documentation
use crate::err::GuestErr;
use crate::err::MechErr;
use crate::{MechtronFactories, MechtronSkel, Platform};
use cosmic_macros::handler_sync;
use cosmic_space::err::SpaceErr;
use cosmic_space::hyper::HyperSubstance;
use cosmic_space::kind::Kind::Mechtron;
use cosmic_space::loc::{Layer, Point, ToSurface};
use cosmic_space::log::{
    LogSource, NoAppender, PointLogger, RootLogger, SynchTransmittingLogAppender,
};
use cosmic_space::particle::Details;
use cosmic_space::wave::core::CoreBounce;
use cosmic_space::wave::exchange::synch::{
    DirectedHandler, DirectedHandlerProxy, DirectedHandlerShell, ExchangeRouter, InCtx,
    ProtoTransmitter, ProtoTransmitterBuilder, RootInCtx,
};
use cosmic_space::wave::exchange::SetStrategy;
use cosmic_space::wave::{Agent, DirectedWave, ReflectedAggregate, ToRecipients, UltraWave};
use dashmap::DashMap;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::Arc;

#[derive(Clone)]
pub struct GuestSkel<P>
where
    P: Platform + 'static,
{
    details: Details,
    mechtrons: Arc<DashMap<Point, HostedMechtron>>,
    factories: Arc<MechtronFactories<P>>,
    logger: PointLogger,
    transmitter: ProtoTransmitterBuilder,
    platform: P,
}

impl<P> GuestSkel<P>
where
    P: Platform + 'static,
{
    pub fn new(details: Details, factories: Arc<MechtronFactories<P>>, platform: P) -> Self {
        let router: GuestRouter<P> = GuestRouter::new();
        let router = Arc::new(router);
        let mut transmitter = ProtoTransmitterBuilder::new(router);
        transmitter.agent = SetStrategy::Override(Agent::Point(details.stub.point.clone()));
        transmitter.from = SetStrategy::Override(details.stub.point.clone().to_surface());
        transmitter.to = SetStrategy::Override(Point::global_logger().to_surface().to_recipients());
        let appender = SynchTransmittingLogAppender::new(transmitter.clone());
        let root_logger = RootLogger::new(LogSource::Core, Arc::new(appender));
        let logger = root_logger.point(details.stub.point.clone());

        let mechtrons = Arc::new(DashMap::new());
        Self {
            details,
            mechtrons,
            factories,
            logger,
            platform,
            transmitter,
        }
    }

    fn hosted(&self, point: &Point) -> Result<HostedMechtron, GuestErr> {
        Ok(self
            .mechtrons
            .get(point)
            .ok_or::<GuestErr>(
                format!(
                    "mechtron associated with point: {} is not hosted by guest {}",
                    point.to_string(),
                    self.details.stub.point.to_string()
                )
                .into(),
            )?
            .value()
            .clone())
    }

    fn mechtron_skel(&self, point: &Point) -> Result<MechtronSkel<P>, GuestErr> {
        let hosted = self.hosted(point)?;
        let mut transmitter = self.builder();
        transmitter.from = SetStrategy::Override(hosted.details.stub.point.to_surface());
        transmitter.agent = SetStrategy::Fill(Agent::Point(hosted.details.stub.point.clone()));
        let logger = self.logger.point(hosted.details.stub.point.clone());
        let phantom: PhantomData<P> = PhantomData::default();
        let skel = MechtronSkel::new(
            hosted.details.clone(),
            logger,
            transmitter.clone().build(),
            phantom,
        );

        Ok(skel)
    }

    pub fn builder(&self) -> ProtoTransmitterBuilder {
        self.transmitter.clone()
    }

    pub fn transmitter(&self) -> ProtoTransmitter {
        self.transmitter.clone().build()
    }
}

pub struct Guest<P>
where
    P: Platform + 'static,
{
    skel: GuestSkel<P>,
}

impl<P> Guest<P>
where
    P: Platform + 'static,
{
    pub fn new(details: Details, platform: P) -> Result<Self, GuestErr>
    where
        P: Sized,
        GuestErr: From<<P as Platform>::Err>,
    {
        let factories = Arc::new(platform.factories()?);
        let skel = GuestSkel::new(details.clone(), factories, platform);
        skel.logger.info(format!("Guest created '{}'", details.stub.point.to_string() ));

        Ok(Self { skel })
    }
}

impl<P> crate::Guest for Guest<P>
where
    P: Platform,
{
    fn handler(&self, point: &Point) -> Result<DirectedHandlerShell, GuestErr> {
        if *point == self.skel.details.stub.point {
            Ok(DirectedHandlerShell::new(
                Box::new(GuestHandler::new(self.skel.clone())),
                self.skel.builder(),
                self.skel.details.stub.point.to_surface(),
                self.skel.logger.logger.clone(),
            ))
        } else {
            let hosted = self.skel.hosted(point)?;
            let factory = self.skel.factories.get(&hosted.name).ok_or(format!(
                "cannot find factory assicated with name: {}",
                hosted.name
            ))?;

            let skel = self.skel.mechtron_skel(point)?;
            let mechtron = factory
                .handler(skel)
                .map_err(|e| GuestErr::from(e.to_string()))?;

            Ok(DirectedHandlerShell::new(
                mechtron,
                self.skel.builder(),
                hosted.details.stub.point.to_surface(),
                self.skel.logger.logger.clone(),
            ))
        }
    }

    fn logger(&self) -> &PointLogger {
        &self.skel.logger
    }
}

pub struct GuestRouter<P>
where
    P: crate::Platform,
{
    phantom: PhantomData<P>,
}

impl<P> GuestRouter<P>
where
    P: crate::Platform,
{
    pub fn new() -> Self {
        Self {
            phantom: PhantomData::default(),
        }
    }
}

impl<P> ExchangeRouter for GuestRouter<P>
where
    P: crate::Platform,
{
    fn route(&self, wave: UltraWave) {
        crate::membrane::mechtron_exchange_wave_host::<P>(wave);
    }

    fn exchange(&self, direct: DirectedWave) -> Result<ReflectedAggregate, SpaceErr> {
        crate::membrane::mechtron_exchange_wave_host::<P>(direct.to_ultra())
            .map_err(|e| e.to_uni_err())
    }
}

pub struct GuestHandler<P>
where
    P: Platform + 'static,
{
    skel: GuestSkel<P>,
}

impl<P> GuestHandler<P>
where
    P: Platform + 'static,
{
    pub fn new(skel: GuestSkel<P>) -> Self {
        Self { skel }
    }
}

#[handler_sync]
impl<P> GuestHandler<P>
where
    P: Platform + 'static,
{
    #[route("Hyp<Host>")]
    pub fn assign(&self, ctx: InCtx<'_, HyperSubstance>) -> Result<(), P::Err> {
        self.skel.logger.info("Received Host command!");
        if let HyperSubstance::Host(host) = ctx.input {
            let factory =
                self.skel
                    .logger
                    .result(self.skel.factories.get(&host.config.name).ok_or(format!(
                        "Guest does not have a mechtron with name: {}",
                        host.config.name
                    )))?;
            self.skel.logger.info("Creating...");
            self.skel.mechtrons.insert(
                host.details.stub.point.clone(),
                HostedMechtron::new(host.details.clone(), host.config.name.clone()),
            );

            let skel = self.skel.mechtron_skel(&host.details.stub.point)?;
            let mechtron = factory.lifecycle(skel)?;
            self.skel.logger.info("Got MechtronLifecycle...");
            let skel = self.skel.mechtron_skel(&host.details.stub.point)?;
            mechtron.create(skel)?;

            Ok(())
        } else {
            Err("expecting Host ".into())
        }
    }
}

#[derive(Clone)]
pub struct HostedMechtron {
    pub details: Details,
    pub name: String,
}

impl HostedMechtron {
    pub fn new(details: Details, name: String) -> Self {
        Self { details, name }
    }
}