use crate::error::{codes, wasm_err};
#[cfg(feature = "streaming_basic")]
use crate::incremental_dfg::IncrementalDFG;
#[cfg(feature = "streaming_basic")]
use crate::incremental_dfg::StreamingDFG;
use crate::models::{
DeclareModel, EventLog, NGramPredictor, PetriNet, StreamingConformanceChecker, TemporalProfile,
DFG, OCEL,
};
#[cfg(feature = "streaming_basic")]
use crate::streaming::{StreamingDfgBuilder, StreamingHeuristicBuilder, StreamingSkeletonBuilder};
#[cfg(feature = "streaming_full")]
use crate::streaming_pipeline::StreamingPipeline;
use once_cell::sync::Lazy;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use wasm_bindgen::prelude::*;
#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
pub enum StoredObject {
EventLog(EventLog),
OCEL(OCEL),
PetriNet(PetriNet),
DFG(DFG),
DeclareModel(DeclareModel),
#[allow(dead_code)]
JsonString(String),
#[cfg(feature = "streaming_basic")]
StreamingDfgBuilder(StreamingDfgBuilder),
#[cfg(feature = "streaming_basic")]
StreamingSkeletonBuilder(StreamingSkeletonBuilder),
#[cfg(feature = "streaming_basic")]
StreamingHeuristicBuilder(StreamingHeuristicBuilder),
StreamingConformanceChecker(StreamingConformanceChecker),
TemporalProfile(TemporalProfile),
NGramPredictor(NGramPredictor),
#[cfg(feature = "streaming_basic")]
IncrementalDFG(IncrementalDFG),
#[cfg(feature = "streaming_basic")]
StreamingDFG(StreamingDFG),
#[cfg(feature = "streaming_full")]
StreamingPipeline(StreamingPipeline),
#[cfg(feature = "powl")]
PowlModel {
arena: crate::powl_arena::PowlArena,
root: u32,
},
}
pub struct AppState {
objects: Arc<Mutex<HashMap<String, StoredObject>>>,
counter: Arc<Mutex<u64>>,
lsa: Arc<Mutex<crate::lsa::LifecycleAuthority>>,
}
impl AppState {
#[must_use]
pub fn new() -> Self {
AppState {
objects: Arc::new(Mutex::new(HashMap::new())),
counter: Arc::new(Mutex::new(0)),
lsa: Arc::new(Mutex::new(crate::lsa::LifecycleAuthority::default())),
}
}
#[must_use]
pub fn store_object(&self, obj: StoredObject) -> Result<String, JsValue> {
let mut counter = self.counter.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock counter: {e}"),
)
})?;
let id = format!("obj_{counter}");
*counter += 1;
let mut objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
objects.insert(id.clone(), obj);
Ok(id)
}
#[must_use]
pub fn get_object(&self, id: &str) -> Result<Option<StoredObject>, JsValue> {
let objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
Ok(objects.get(id).cloned())
}
#[must_use]
pub fn with_object<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(Option<&StoredObject>) -> Result<R, JsValue>,
{
let objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
f(objects.get(id))
}
#[must_use]
pub fn with_object_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(Option<&mut StoredObject>) -> Result<R, JsValue>,
{
let mut objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
f(objects.get_mut(id))
}
#[must_use]
pub fn with_event_log<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&EventLog) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::EventLog(log)) => f(log),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not an EventLog")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("EventLog '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_petri_net<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&PetriNet) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::PetriNet(net)) => f(net),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not a PetriNet")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("PetriNet '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_petri_net_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut PetriNet) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::PetriNet(net)) => f(net),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not a PetriNet")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("PetriNet '{id}' not found"),
)),
})
}
#[cfg(feature = "ocel")]
pub fn with_ocel<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&OCEL) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::OCEL(ocel)) => f(ocel),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not an OCEL")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("OCEL '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_dfg<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&DFG) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::DFG(dfg)) => f(dfg),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not a DFG")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("DFG '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_json_string<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&str) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::JsonString(s)) => f(s),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not a JsonString")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("JsonString '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_event_log_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut EventLog) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::EventLog(log)) => f(log),
Some(_) => Err(wasm_err(codes::INVALID_INPUT, "Object is not an EventLog")),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("EventLog '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_basic")]
pub fn with_streaming_dfg<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&StreamingDfgBuilder) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::StreamingDfgBuilder(b)) => f(b),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingDfgBuilder",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingDfgBuilder '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_basic")]
pub fn with_streaming_dfg_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut StreamingDfgBuilder) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::StreamingDfgBuilder(b)) => f(b),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingDfgBuilder",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingDfgBuilder '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_temporal_profile<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&TemporalProfile) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::TemporalProfile(p)) => f(p),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a TemporalProfile",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("TemporalProfile '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_declare_model<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&DeclareModel) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::DeclareModel(m)) => f(m),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a DeclareModel",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("DeclareModel '{id}' not found"),
)),
})
}
#[must_use]
pub fn with_streaming_conformance_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut StreamingConformanceChecker) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::StreamingConformanceChecker(c)) => f(c),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingConformanceChecker",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingConformanceChecker '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_basic")]
pub fn with_streaming_skeleton_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut StreamingSkeletonBuilder) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::StreamingSkeletonBuilder(b)) => f(b),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingSkeletonBuilder",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingSkeletonBuilder '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_basic")]
pub fn with_streaming_skeleton<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&StreamingSkeletonBuilder) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::StreamingSkeletonBuilder(b)) => f(b),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingSkeletonBuilder",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingSkeletonBuilder '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_basic")]
pub fn with_streaming_heuristic_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut StreamingHeuristicBuilder) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::StreamingHeuristicBuilder(b)) => f(b),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingHeuristicBuilder",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingHeuristicBuilder '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_basic")]
pub fn with_streaming_heuristic<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&StreamingHeuristicBuilder) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::StreamingHeuristicBuilder(b)) => f(b),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingHeuristicBuilder",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingHeuristicBuilder '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_full")]
pub fn with_streaming_pipeline_mut<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&mut StreamingPipeline) -> Result<R, JsValue>,
{
self.with_object_mut(id, |obj| match obj {
Some(StoredObject::StreamingPipeline(p)) => f(p),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingPipeline",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingPipeline '{id}' not found"),
)),
})
}
#[cfg(feature = "streaming_full")]
pub fn with_streaming_pipeline<F, R>(&self, id: &str, f: F) -> Result<R, JsValue>
where
F: FnOnce(&StreamingPipeline) -> Result<R, JsValue>,
{
self.with_object(id, |obj| match obj {
Some(StoredObject::StreamingPipeline(p)) => f(p),
Some(_) => Err(wasm_err(
codes::INVALID_INPUT,
"Object is not a StreamingPipeline",
)),
None => Err(wasm_err(
codes::INVALID_HANDLE,
format!("StreamingPipeline '{id}' not found"),
)),
})
}
#[must_use]
pub fn delete_object(&self, id: &str) -> Result<bool, JsValue> {
let mut objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
Ok(objects.remove(id).is_some())
}
#[must_use]
pub fn object_count(&self) -> Result<usize, JsValue> {
let objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
Ok(objects.len())
}
#[must_use]
pub fn clear_all(&self) -> Result<(), JsValue> {
let mut objects = self.objects.lock().map_err(|e| {
wasm_err(
codes::INTERNAL_ERROR,
format!("Failed to lock objects: {e}"),
)
})?;
objects.clear();
Ok(())
}
}
impl Default for AppState {
fn default() -> Self {
Self::new()
}
}
static APP_STATE: Lazy<AppState> = Lazy::new(AppState::new);
pub fn get_or_init_state() -> &'static AppState {
&APP_STATE
}
#[wasm_bindgen]
#[must_use]
pub fn delete_object(id: &str) -> Result<bool, JsValue> {
get_or_init_state().delete_object(id)
}
#[wasm_bindgen]
pub fn object_exists(id: &str) -> bool {
get_or_init_state()
.with_object(id, |obj| Ok(obj.is_some()))
.unwrap_or(false)
}
#[wasm_bindgen]
#[must_use]
pub fn object_count() -> Result<usize, JsValue> {
get_or_init_state().object_count()
}
#[wasm_bindgen]
#[must_use]
pub fn clear_all_objects() -> Result<(), JsValue> {
get_or_init_state().clear_all()
}