use crate::{
dispatch::{Action, ActionDispatcher},
engine_core::EngineCore,
error::EngineError,
ir::{ComponentRegistry, IRNode, ResourceRegistry},
lifecycle::{ModuleInstance, ResourceCache},
reconcile::Patch,
state::StateChange,
};
use indexmap::IndexMap;
use std::sync::{Arc, Weak};
pub type RenderCallback = Box<dyn Fn(&[Patch]) + Send + Sync>;
pub struct Engine {
core: EngineCore,
actions: ActionDispatcher,
resources: ResourceCache,
render_callback: Option<RenderCallback>,
last_state_weak: Weak<serde_json::Value>,
}
impl Engine {
pub fn new() -> Self {
Self {
core: EngineCore::new(),
actions: ActionDispatcher::new(),
resources: ResourceCache::new(),
render_callback: None,
last_state_weak: Weak::new(),
}
}
pub fn register_component(&mut self, component: crate::ir::Component) {
self.core.register_component(component);
}
pub fn set_component_resolver<F>(&mut self, resolver: F)
where
F: Fn(&str, Option<&str>) -> Option<crate::ir::ResolvedComponent> + Send + Sync + 'static,
{
self.core.set_component_resolver(resolver);
}
pub fn register_resource(&mut self, name: &str, svg: &str) {
self.core.register_resource(name, svg);
}
pub fn register_resources(&mut self, map: IndexMap<String, String>) {
self.core.register_resources(map);
}
pub fn resource_registry(&self) -> &ResourceRegistry {
&self.core.resource_registry
}
pub fn resource_registry_mut(&mut self) -> &mut ResourceRegistry {
&mut self.core.resource_registry
}
pub fn icon_registry(&self) -> &ResourceRegistry {
&self.core.resource_registry
}
pub fn icon_registry_mut(&mut self) -> &mut ResourceRegistry {
&mut self.core.resource_registry
}
pub fn set_module(&mut self, module: ModuleInstance) {
self.core.set_module(module);
}
pub fn register_module(&mut self, name: impl Into<String>, module: ModuleInstance) {
self.core.register_module(name, module);
}
pub fn get_module_state(&self, name: &str) -> Option<&serde_json::Value> {
self.core.get_module_state(name)
}
pub fn modules(&self) -> &IndexMap<String, ModuleInstance> {
&self.core.modules
}
pub fn set_render_callback<F>(&mut self, callback: F)
where
F: Fn(&[Patch]) + Send + Sync + 'static,
{
self.render_callback = Some(Box::new(callback));
}
pub fn on_action<F>(&mut self, action_name: impl Into<String>, handler: F)
where
F: Fn(&Action) + Send + Sync + 'static,
{
self.actions.on(action_name, handler);
}
pub fn render(&mut self, element: &crate::ir::Element) {
let ir_node = IRNode::Element(element.clone());
self.render_ir_node(&ir_node);
}
pub fn render_ir_node(&mut self, ir_node: &IRNode) {
let patches = self.core.render_ir_node(ir_node);
self.emit_patches(patches);
}
pub fn notify_state_change(&mut self, change: &StateChange) {
if let Some(ref module) = self.core.module {
let current = module.get_state_shared();
if self.core.revision > 0 {
if let Some(prev) = self.last_state_weak.upgrade() {
if Arc::ptr_eq(&prev, ¤t) {
return;
}
}
}
self.last_state_weak = Arc::downgrade(¤t);
}
self.core.schedule_from_state_change(change);
self.render_dirty();
}
pub fn update_state(&mut self, scope: Option<&str>, state_patch: serde_json::Value) {
if self.core.update_state(scope, state_patch) {
self.render_dirty();
}
}
pub fn update_state_sparse(
&mut self,
scope: Option<&str>,
paths: &[String],
values: &serde_json::Value,
) {
if self.core.update_state_sparse(scope, paths, values) {
self.render_dirty();
}
}
pub fn dispatch_action(&mut self, action: Action) -> Result<(), EngineError> {
self.actions.dispatch(&action)
}
pub fn action_scope_for(&self, action_name: &str) -> Option<String> {
self.core.action_scope_for(action_name)
}
fn render_dirty(&mut self) {
let patches = self.core.render_dirty();
if !patches.is_empty() {
self.emit_patches(patches);
}
}
fn emit_patches(&self, mut patches: Vec<Patch>) {
EngineCore::filter_spurious_removes(&mut patches);
if let Some(ref callback) = self.render_callback {
callback(&patches);
}
}
pub fn revision(&self) -> u64 {
self.core.revision
}
pub fn component_registry(&self) -> &ComponentRegistry {
&self.core.component_registry
}
pub fn component_registry_mut(&mut self) -> &mut ComponentRegistry {
&mut self.core.component_registry
}
pub fn resources(&self) -> &ResourceCache {
&self.resources
}
pub fn resources_mut(&mut self) -> &mut ResourceCache {
&mut self.resources
}
pub fn set_context(&mut self, name: &str, data: serde_json::Value) {
self.core.set_context(name, data);
self.render_dirty();
}
pub fn remove_context(&mut self, name: &str) {
self.core.remove_context(name);
self.render_dirty();
}
pub fn data_sources(&self) -> &indexmap::IndexMap<String, serde_json::Value> {
&self.core.data_sources
}
}
impl Default for Engine {
fn default() -> Self {
Self::new()
}
}