#[cfg(feature = "native")]
pub mod operator;
mod run;
pub mod runtime;
#[cfg(feature = "studio-bridge")]
mod studio;
#[cfg(feature = "tui")]
pub mod tui;
#[cfg(feature = "native")]
pub use run::{run, run_with, run_with_frontend, run_with_hal};
pub use runtime::{RuntimeError, StepOutcome, Telemetry, TelemetrySnapshot};
pub use arora_behavior_tree::behavior::BehaviorTreeInterpreter;
use anyhow::Result;
use arora_behavior::{golden, BehaviorInterpreter};
use arora_behavior_tree::ModuleFunction;
use arora_bridge::{Bridge, BridgeError, Inbound, InboundStream};
use arora_engine::engine::{EngineBuilder, PinnedEngine};
#[cfg(feature = "native")]
use arora_engine::executor::{native::NativeExecutor, wasm::WebAssemblyExecutor};
use arora_hal::{FakeHal, Hal, UpdatesStream};
use arora_simple_data_store::SimpleDataStore;
use arora_types::data::{DataStore, Subscription};
use futures::stream::{self, Fuse, SelectAll};
use futures::StreamExt;
use runtime::{Clock, Pending};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use uuid::Uuid;
pub struct Arora {
pub(crate) store: Box<dyn DataStore>,
pub(crate) engine: PinnedEngine,
pub(crate) function_index: Rc<HashMap<Uuid, ModuleFunction>>,
pub(crate) interpreter: runtime::InterpreterCell,
pub(crate) telemetry: Telemetry,
pub(crate) hal: Box<dyn Hal>,
pub(crate) bridges: Vec<Box<dyn Bridge>>,
pub(crate) hal_feed: Fuse<UpdatesStream>,
pub(crate) inbound: SelectAll<InboundStream>,
pub(crate) pending: Pending,
pub(crate) store_changes: Subscription,
pub(crate) clock: Clock,
}
impl Arora {
pub fn builder() -> AroraBuilder {
AroraBuilder::default()
}
pub fn store(&self) -> &dyn DataStore {
&*self.store
}
}
#[derive(Default)]
pub struct AroraBuilder {
store: Option<Box<dyn DataStore>>,
hal: Option<Box<dyn Hal>>,
bridges: Vec<Box<dyn Bridge>>,
interpreter: Option<Box<dyn BehaviorInterpreter>>,
functions: HashMap<Uuid, ModuleFunction>,
}
impl AroraBuilder {
pub fn with_data_store(mut self, store: Box<dyn DataStore>) -> Self {
self.store = Some(store);
self
}
pub fn with_hal(mut self, hal: Box<dyn Hal>) -> Self {
self.hal = Some(hal);
self
}
pub fn with_bridge(mut self, bridge: Box<dyn Bridge>) -> Self {
self.bridges.push(bridge);
self
}
pub fn with_behavior_interpreter(mut self, interpreter: Box<dyn BehaviorInterpreter>) -> Self {
self.interpreter = Some(interpreter);
self
}
pub fn with_module(mut self, functions: impl IntoIterator<Item = ModuleFunction>) -> Self {
for function in functions {
self.functions.insert(function.function_id, function);
}
self
}
pub fn build(self) -> Result<Arora> {
let mut engine = build_engine()?;
let store = self
.store
.unwrap_or_else(|| Box::new(SimpleDataStore::new()));
let hal: Box<dyn Hal> = self.hal.unwrap_or_else(|| Box::new(FakeHal::new()));
let store_changes = store.subscribe();
let hal_feed = hal.updates().fuse();
let mut bridges = self.bridges;
let mut inbound = SelectAll::new();
for bridge in &mut bridges {
let disconnected = stream::once(async {
Inbound::DeviceInfo(Err(BridgeError::Disconnected(
"the endpoint's inbound stream ended".into(),
)))
});
inbound.push(bridge.take_inbound().chain(disconnected).boxed() as InboundStream);
}
let function_index = Rc::new(self.functions);
let interpreter = self
.interpreter
.unwrap_or_else(|| Box::new(BehaviorTreeInterpreter::new(function_index.clone())));
let interpreter: runtime::InterpreterCell = Rc::new(RefCell::new(Some(interpreter)));
engine.register_host_function(
golden::BEHAVIOR_MODULE,
golden::APPLY_DIFF,
Rc::new(runtime::GoldenEdit {
interpreter: interpreter.clone(),
}),
);
Ok(Arora {
store,
engine,
function_index,
interpreter,
telemetry: Telemetry::default(),
hal,
bridges,
hal_feed,
inbound,
pending: Pending::default(),
store_changes,
clock: Clock::default(),
})
}
}
#[cfg(feature = "native")]
fn build_engine() -> Result<PinnedEngine> {
Ok(EngineBuilder::new()
.add_executor(
WebAssemblyExecutor::new()
.map_err(|e| anyhow::anyhow!("failed to create wasm executor: {e}"))?,
)
.add_executor(NativeExecutor::new())
.build())
}
#[cfg(not(feature = "native"))]
fn build_engine() -> Result<PinnedEngine> {
use arora_engine::executor::browser::BrowserExecutor;
Ok(EngineBuilder::new()
.add_executor(BrowserExecutor::new())
.build())
}