Skip to main content

mechtron/
lib.rs

1#![allow(warnings)]
2
3pub mod err;
4pub mod guest;
5pub mod membrane;
6pub mod space;
7#[cfg(test)]
8pub mod test;
9
10#[macro_use]
11extern crate lazy_static;
12
13#[macro_use]
14extern crate cosmic_macros;
15
16#[macro_use]
17extern crate cosmic_macros_primitive;
18
19extern crate alloc;
20extern crate core;
21
22use alloc::boxed::Box;
23use alloc::string::String;
24use alloc::sync::Arc;
25use core::str::FromStr;
26use cosmic_macros::handler;
27use cosmic_macros::route;
28use cosmic_macros::DirectedHandler;
29use cosmic_space::err::SpaceErr;
30use cosmic_space::hyper::HyperSubstance;
31use cosmic_space::loc::{Layer, ToSurface, Uuid};
32use cosmic_space::log::{LogSource, NoAppender, PointLogger, RootLogger};
33use cosmic_space::parse::SkewerCase;
34use cosmic_space::particle::{Details, Stub};
35use cosmic_space::wasm::Timestamp;
36use cosmic_space::wave::exchange::SetStrategy;
37use cosmic_space::wave::{Agent, DirectedWave, ReflectedAggregate, ReflectedWave, UltraWave};
38use cosmic_space::{loc, VERSION};
39use std::collections::HashMap;
40use std::marker::PhantomData;
41use std::sync::mpsc::Sender;
42use std::sync::{mpsc, MutexGuard};
43
44use cosmic_space::wave::Bounce;
45
46use cosmic_space::artifact::synch::ArtifactApi;
47use cosmic_space::artifact::ArtRef;
48use cosmic_space::wave::exchange::synch::{
49    DirectedHandler, DirectedHandlerProxy, DirectedHandlerShell, ExchangeRouter, InCtx,
50    ProtoTransmitter, ProtoTransmitterBuilder,
51};
52use std::sync::RwLock;
53use cosmic_space::point::Point;
54
55use crate::err::{GuestErr, MechErr};
56use crate::guest::GuestCtx;
57use crate::membrane::{mechtron_frame_to_host, mechtron_timestamp, mechtron_uuid};
58
59#[no_mangle]
60extern "C" {
61    pub fn mechtron_guest(details: Details) -> Result<Arc<dyn Guest>, GuestErr>;
62}
63
64pub trait Guest: Send + Sync {
65    fn handler(&self, point: &Point) -> Result<DirectedHandlerShell, GuestErr>;
66    fn logger(&self) -> &PointLogger;
67}
68
69pub trait Platform: Clone + Send + Sync
70where
71    Self::Err: MechErr,
72{
73    type Err;
74    fn factories(&self) -> Result<MechtronFactories<Self>, Self::Err>
75    where
76        Self: Sized,
77    {
78        Ok(MechtronFactories::new())
79    }
80}
81
82pub struct MechtronFactories<P>
83where
84    P: Platform,
85{
86    factories: HashMap<String, RwLock<Box<dyn MechtronFactory<P>>>>,
87    phantom: PhantomData<P>,
88}
89
90impl<P> MechtronFactories<P>
91where
92    P: Platform,
93{
94    pub fn new() -> Self {
95        Self {
96            factories: HashMap::new(),
97            phantom: Default::default(),
98        }
99    }
100    pub fn add<F>(&mut self, factory: F)
101    where
102        F: MechtronFactory<P>,
103    {
104        SkewerCase::from_str(factory.name().as_str() ).expect("Mechtron Name must be valid kebab (skewer) case (all lower case alphanumeric and dashes with leading letter)");
105        self.factories
106            .insert(factory.name(), RwLock::new(Box::new(factory)));
107    }
108
109    pub fn get<S>(&self, name: S) -> Option<&RwLock<Box<dyn MechtronFactory<P>>>>
110    where
111        S: ToString,
112    {
113        self.factories.get(&name.to_string())
114    }
115}
116
117pub trait MechtronFactory<P>: Sync + Send + 'static
118where
119    P: Platform,
120{
121    fn name(&self) -> String;
122    fn new(&mut self, skel: MechtronSkel<P>) -> Result<(), P::Err>;
123    fn lifecycle(&self, skel: MechtronSkel<P>) -> Result<Box<dyn MechtronLifecycle<P>>, P::Err>;
124    fn handler(&self, skel: MechtronSkel<P>) -> Result<Box<dyn DirectedHandler>, P::Err>;
125}
126
127/// The MechtronSkel holds the common static elements of the Mechtron together
128/// Since a Mechtron is always an instance created to handle a single
129/// Directed Wave or Init, the Skel is cloned and passed to each
130/// Mechtron instance.
131///
132#[derive(Clone)]
133pub struct MechtronSkel<P>
134where
135    P: Platform,
136{
137    pub details: Details,
138    pub logger: PointLogger,
139    pub artifacts: ArtifactApi,
140    phantom: PhantomData<P>,
141}
142
143impl<P> MechtronSkel<P>
144where
145    P: Platform,
146{
147    pub fn new(
148        details: Details,
149        logger: PointLogger,
150        phantom: PhantomData<P>,
151        artifacts: ArtifactApi,
152    ) -> Self {
153        let logger = logger.point(details.stub.point.clone());
154        Self {
155            details,
156            logger,
157            phantom,
158            artifacts,
159        }
160    }
161    pub fn bundle(&self) -> Result<Point, P::Err> {
162        let config = self
163            .details
164            .properties
165            .get("config")
166            .ok_or::<P::Err>("expecting mechtron to have config property set".into())?;
167        let config = Point::from_str(config.value.as_str())?;
168        let bundle = config.to_bundle()?.push(":/")?;
169        Ok(bundle)
170    }
171
172    pub fn raw_from_bundle<S: ToString>(&self, path: S) -> Result<ArtRef<Vec<u8>>, P::Err> {
173        let point = self.bundle()?.push(path)?;
174        Ok(self.artifacts.raw(&point)?)
175    }
176}
177
178/// MechtronLifecycle is the interface used by Guest
179/// to make important calls to the Mechtron
180pub trait MechtronLifecycle<P>: DirectedHandler + Sync + Send
181where
182    P: Platform,
183{
184    fn create(&self, _skel: MechtronSkel<P>) -> Result<(), P::Err> {
185        Ok(())
186    }
187}
188
189/// Create a Mechtron by implementing this trait.
190/// Mechtrons are created per request and disposed of afterwards...
191/// Implementers of this trait should only hold references to
192/// Mechtron::Skel, Mechtron::Cache & Mechtron::State at most.
193pub trait Mechtron<P>: MechtronLifecycle<P> + Sync + Send + 'static
194where
195    P: Platform,
196{
197    /// it is recommended to implement MechtronSkel or some derivative
198    /// of MechtronSkel. Skel holds info about the Mechtron (like it's Point,
199    /// exact Kind & Properties)  The Skel may also provide access to other
200    /// services within the Guest. If your Mechtron doesn't use the Skel
201    /// then implement ```type Skel=()```
202    type Skel;
203
204    /// Is any static data (templates, config files) that does not change
205    /// and may need to be reused. If your Mechtron doesn't need a Cache
206    /// then implement ```type Cache=()``
207    type Cache;
208
209    /// State is the aspect of the Mechtron that is changeable.  It is recommended
210    /// to wrap State in a Mutex or RwLock if used.  If you are implementing
211    /// a stateless mechtron then implement ```type State=();```
212    type State;
213
214    /// This method is called by a companion MechtronFactory implementation
215    /// to bring this Mechtron back to life to handle an Init or a Directed Wave
216    fn restore(skel: Self::Skel, cache: Self::Cache, state: Self::State) -> Self;
217
218    /// create the Cache for this Mechtron (templates, configs & static content)
219    /// the cache should hold any static content that is expected to be unchanging
220    fn cache(_skel: Self::Skel) -> Result<Option<Self::Cache>, P::Err> {
221        Ok(None)
222    }
223}